如何利用异步Task的结果处理事件?求现有方案外的优化思路
更优雅的异步+事件处理方案
你的当前实现依赖共享状态变量协调「狗粮到货」和「狗饥饿」两个事件,存在线程安全隐患,且逻辑分散在不同回调中,维护成本高。下面是更优的实现思路,利用Task异步机制替代共享状态,让逻辑更集中、安全。
核心优化思路
把「狗饥饿事件」通过TaskCompletionSource包装成Task,然后同时等待「狗粮订购Task」和「饥饿Task」,根据哪个先完成执行对应逻辑,完全不需要共享状态变量。
优化后的代码
class Program { public static async Task Main() { Print("开始。"); var dog = new Dog(); // 用TaskCompletionSource将饥饿事件转为可等待的Task var hungryTaskSource = new TaskCompletionSource<Dog>(); dog.Hungry += (sender, _) => hungryTaskSource.TrySetResult(sender as Dog); // 发起狗粮订购(无需额外Task.Run,async方法本身返回Task) var foodTask = new Amazon().OrderDogFood(); // 等待两个任务中的任意一个完成 var completedTask = await Task.WhenAny(foodTask, hungryTaskSource.Task); if (completedTask == foodTask) { // 狗粮先到:等狗饿了再喂 var food = await foodTask; Print("狗粮已送达,等待狗狗饥饿..."); var hungryDog = await hungryTaskSource.Task; Print("狗粮到后喂狗"); hungryDog.Feed(food); } else { // 狗先饿:等狗粮到了再喂 var hungryDog = await hungryTaskSource.Task; Print("狗狗饿了,等待狗粮配送..."); var food = await foodTask; Print("狗粮到后喂狗"); hungryDog.Feed(food); } Console.ReadKey(); } public static void Print(string msg) { Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + ": " + msg); } } // 以下类保持原实现不变 class Amazon { public async Task<DogFood> OrderDogFood() { var r = new Random(); await Task.Delay(r.Next(500, 5000)); Program.Print("亚马逊配送狗粮到达。"); return new DogFood(); } } class Dog { public event EventHandler Hungry; private bool IsHungry = false; public Dog() { _ = Task.Run(async () => { var r = new Random(); await Task.Delay(r.Next(500, 5000)); Program.Print("狗狗饿了。"); IsHungry = true; Hungry?.Invoke(this, null); }); } public void Feed(DogFood food) { if(!IsHungry) { throw new ApplicationException("狗狗不饿。"); } } } class DogFood { }
方案优势
- 无共享状态,线程安全:避免原代码中静态变量的竞态风险,
TaskCompletionSource和Task本身是线程安全的 - 逻辑集中:所有协调逻辑都在
Main方法中,无需分散到事件回调和Task内部,可读性更强 - 异步原生支持:利用.NET异步机制自然处理两种时序情况,无需手动维护标记变量
- 代码简洁:去掉冗余的状态变量,减少出错点
内容的提问来源于stack exchange,提问作者Damn Vegetables
相关产品推荐
相关产品推荐

