协程锁

协程锁机制 - ET社区 (et-framework.cn)

举个客户端的例子,资源加载ab包,ab包假如使用异步加载,那可能出现一个协程正在加载ab包,还没加载完,另外协程又开始加载这个ab包,那就出问题了。我们显然希望第二个协程要等待第一个协程加载完成,再往下执行,这样可以判断这个包加载过了就不需要再加载了。

核心API

CoroutineLockComponent.Instance.Wait(CoroutineLockType,Key,OuterTime);

  • CoroutineLockType 协程锁类型,是一个Enum

    public static class CoroutineLockType
    {
        public const int None = 0;
        public const int Location = 1;                  // location进程上使用
        public const int ActorLocationSender = 2;       // ActorLocationSender中队列消息 
        public const int Mailbox = 3;                   // Mailbox中队列
        public const int UnitId = 4;                    // Map服务器上线下线时使用
        public const int DB = 5;
        public const int Resources = 6;
        public const int ResourcesLoader = 7;
        public const int LoadUIBaseWindows = 8;
      
        public const int Max = 100; // 这个必须最大
    }
    
  • Key 加锁的唯一Key,Int
  • OuterTime 失效时间,超过这个时间默认解锁,Int,默认60000(毫秒)

核心调用如下:

public static async ETTask<CoroutineLock> Wait(...)
{
    // 1.获取到对应的队列
    CoroutineLockQueueType coroutineLockQueueType = self.list[coroutineLockType];
	// 2.如果不存在这个队列,说明不需要排队等待
    if (!coroutineLockQueueType.TryGetValue(key, out CoroutineLockQueue queue))
    {
        // 2.1直接返回CoroutineLock
        coroutineLockQueueType.Add(key, self.AddChildWithId<CoroutineLockQueue>(++self.idGenerator, true));
        return self.CreateCoroutineLock(coroutineLockType, key, time, 1);
    }
	// 3.加入排队队列
    ETTask<CoroutineLock> tcs = ETTask<CoroutineLock>.Create(true);
    queue.Add(tcs, time);
    return await tcs;
}

List的结构如下

总的来说就是一个List<Dic<Type,Queue»结构

首先对不同类型的CoroutineLockType划分不同的字典,每一个字典中又对不同的Key维护了不同的队列

内部对不同的Key划分了不同的Queue,Key象征的一把锁,同一把锁的调用自然需要进入同一类型的Queue中排队

public List<CoroutineLockQueueType> list;
public class CoroutineLockQueueType: Entity, IAwake, IDestroy
{
    public Dictionary<long, CoroutineLockQueue> dictionary;
}
public class CoroutineLockQueue: Entity, IAwake, IDestroy
{
    public Queue<CoroutineLockInfo> queue = new Queue<CoroutineLockInfo>();
}
public struct CoroutineLockInfo
{
    public ETTask<CoroutineLock> Tcs;
    public int Time;
}

至于锁队列,依赖两部分驱动

一部分是超时处理,这部分在Update中,会定期处理超时的锁,并推进Queue

当然这属于“异常”了,另外一个推进的地方则是在某一个CoroutineLock被销毁时

image-20230817141102015

此时也会推进某一个Queue

因此我们在用完之后,必须对锁进行销毁

image-20230817141218664