using System.Collections.Generic; using Fort23.Core; namespace Fort23.UTool { public class CoroutineLockComponent : Entity { public static CoroutineLockComponent Instance { get; set; } public Dictionary> dictionary = new Dictionary>(); public Queue nextFrameRun = new Queue(); public MultiMap timers = new MultiMap(); public Queue timeOutIds = new Queue(); public Queue timerOutTimer = new Queue(); public long idGenerator; public long minTime; [CustomMethod(CustomMethodType.Awake)] public void Awake() { Instance = this; } [CustomMethod(CustomMethodType.Destroy)] public void Destroy( ) { this.dictionary.Clear(); this.nextFrameRun.Clear(); this.timers.Clear(); this.timeOutIds.Clear(); this.timerOutTimer.Clear(); this.idGenerator = 0; this.minTime = 0; } [CustomMethod(CustomMethodType.Update)] public void Update() { CoroutineLockComponent self = this; // 检测超时的CoroutineLock TimeoutCheck(self); int count = self.nextFrameRun.Count; // 注意这里不能将this.nextFrameRun.Count 放到for循环中,因为循环过程中会有对象继续加入队列 for (int i = 0; i < count; ++i) { CoroutineLock key = self.nextFrameRun.Dequeue(); self.Notify(key.key, key.keyName,0); } } public void TimeoutCheck(CoroutineLockComponent self) { // 超时的锁 if (self.timers.Count == 0) { return; } long timeNow = TimeHelper.ClientNow(); if (timeNow < self.minTime) { return; } foreach (KeyValuePair> kv in self.timers) { long k = kv.Key; if (k > timeNow) { self.minTime = k; break; } self.timeOutIds.Enqueue(k); } self.timerOutTimer.Clear(); while (self.timeOutIds.Count > 0) { long time = self.timeOutIds.Dequeue(); foreach (CoroutineLock coroutineLock in self.timers[time]) { self.timerOutTimer.Enqueue(coroutineLock); } self.timers.Remove(time); } while (self.timerOutTimer.Count > 0) { CoroutineLock coroutineLock = self.timerOutTimer.Dequeue(); // 超时直接调用下一个锁 self.NextFrameRun(coroutineLock); coroutineLock.isTimeout = true; } } public async CTask Wait(string keyName, int time = 10000) { long key = keyName.GetHashCode(); if (!dictionary.TryGetValue(key, out Queue queue)) { // 首次进入,不需要await锁。 dictionary.Add(key, new Queue()); return CreateCoroutineLock(key, keyName,time, 1); } // 60秒的默认超时间隔期间有新的协程访问同样的资源则需要等待前面的锁解除才可以继续。 CTask tcs = CTask.Create(false); queue.Enqueue(new CoroutineLockTimer() {tcs = tcs, duration = time}); return await tcs; } public CoroutineLock CreateCoroutineLock(long key,string keyName, int time, int count) { CoroutineLock coroutineLock = AddChildWithId(++idGenerator, key, keyName,count, true); if (time > 0) { AddTimer(TimeHelper.ClientNow() + time, coroutineLock); } return coroutineLock; } public void AddTimer(long tillTime, CoroutineLock coroutineLock) { timers.Add(tillTime, coroutineLock); if (tillTime < minTime) { minTime = tillTime; } } public void NextFrameRun(CoroutineLock key) { nextFrameRun.Enqueue(key); } public void Notify(long key, string keyName,int count) { if (!dictionary.TryGetValue(key, out Queue queue)) { return; } if (queue.Count == 0) { dictionary.Remove(key); return; } // TODO 这个地方暂时不是很清楚作用,猜测是和复用或者是重置计数有关的。 // const int frameCoroutineCount = 10; // if (count > frameCoroutineCount) // { // NextFrameRun(key); // return; // } CoroutineLockTimer coroutineLockTimer = queue.Dequeue(); coroutineLockTimer.tcs.SetResult(CreateCoroutineLock(key,keyName, coroutineLockTimer.duration, count)); } } }