using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Model { public abstract class LocationTask : SceneEntity { protected LocationTask() { } protected LocationTask(long id) : base(id) { } public abstract void Run(); } public sealed class LocationLockTask : LocationTask { private readonly long key; private readonly int time; private readonly TaskCompletionSource tcs; public LocationLockTask(long key, int time) { this.key = key; this.time = time; this.tcs = new TaskCompletionSource(); } public Task Task { get { return this.tcs.Task; } } public override void Run() { try { Scene.GetComponent().Lock(this.key, this.time); this.tcs.SetResult(true); } catch (Exception e) { this.tcs.SetException(e); } } } public sealed class LocationQueryTask : LocationTask { private readonly long key; private readonly TaskCompletionSource tcs; public LocationQueryTask(long key) { this.key = key; this.tcs = new TaskCompletionSource(); } public Task Task { get { return this.tcs.Task; } } public override void Run() { try { string location = Scene.GetComponent().Get(key); this.tcs.SetResult(location); } catch (Exception e) { this.tcs.SetException(e); } } } public class LocationComponent : Component { private readonly Dictionary locations = new Dictionary(); private readonly Dictionary lockDict = new Dictionary(); private readonly Dictionary> taskQueues = new Dictionary>(); public async void Add(long key, string address) { this.locations[key] = address; // 更新db await Game.Scene.GetComponent().Save(new Location(key, address)); } public void Remove(long key) { this.locations.Remove(key); } public string Get(long key) { this.locations.TryGetValue(key, out string location); return location; } public async void Lock(long key, int appId, int time = 0) { if (this.lockDict.ContainsKey(key)) { return; } this.lockDict.Add(key, appId); // 超时则解锁 if (time > 0) { await Game.Scene.GetComponent().WaitAsync(time); int saveAppId = 0; this.lockDict.TryGetValue(key, out saveAppId); if (saveAppId != appId) { Log.Warning($"unlock appid is different {saveAppId} {appId}"); return; } this.UnLock(key); } } public void UpdateAndUnLock(long key, int appId, string value) { int saveAppId = 0; this.lockDict.TryGetValue(key, out saveAppId); if (saveAppId != appId) { Log.Warning($"unlock appid is different {saveAppId} {appId}" ); return; } this.Add(key, value); this.UnLock(key); } private void UnLock(long key) { this.lockDict.Remove(key); if (!this.taskQueues.TryGetValue(key, out Queue tasks)) { return; } while (true) { if (tasks.Count <= 0) { this.taskQueues.Remove(key); return; } if (this.lockDict.ContainsKey(key)) { return; } LocationTask task = tasks.Dequeue(); task.Run(); } } public Task LockAsync(long key, int time) { if (!this.lockDict.ContainsKey(key)) { this.Lock(key, time); return Task.FromResult(true); } LocationLockTask task = new LocationLockTask(key, time); this.AddTask(key, task); return task.Task; } public Task GetAsync(long key) { if (!this.lockDict.ContainsKey(key)) { this.locations.TryGetValue(key, out string location); return Task.FromResult(location); } LocationQueryTask task = new LocationQueryTask(key); this.AddTask(key, task); return task.Task; } public void AddTask(long key, LocationTask task) { if (!this.taskQueues.TryGetValue(key, out Queue tasks)) { tasks = new Queue(); this.taskQueues[key] = tasks; } task.Scene = this.GetOwner(); tasks.Enqueue(task); } public override void Dispose() { if (this.Id == 0) { return; } base.Dispose(); } } }