C#中Async和Await未返回预期结果的问题排查
问题描述
在C#的Main方法中调用了4个同步任务(Task1-Task4)和4个异步任务(Task5-Task8),同步任务执行正常,但异步任务仅启动却未完成。同步任务使用Task.Delay().Wait()实现延迟,异步任务通过async void结合Task.Run与Thread.Sleep实现,期望得到如下指定输出结果,询问导致异步任务无法完成的原因。
Main方法代码
public static void Main(string[] args) { // Synchronous methods Task1(); Task2(); Task3(); Task4(); // Asynchronous methods Task5(); Task6(); Task7(); Task8(); }
任务实现代码
public static void Task1() { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 1..."); Task.Delay(4000).Wait(); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 1!"); } public static void Task2() { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 2..."); Task.Delay(2000).Wait(); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 2!"); } public static void Task3() { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 3..."); Task.Delay(5000).Wait(); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 3!"); } public static void Task4() { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 4..."); Task.Delay(1000).Wait(); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 4!"); } public static async void Task5() { await Task.Run(() => { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 5..."); Thread.Sleep(4000); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 5!"); }); } public static async void Task6() { await Task.Run(() => { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 6..."); Thread.Sleep(2000); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 6!"); }); } public static async void Task7() { await Task.Run(() => { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 7..."); Thread.Sleep(5000); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 7!"); }); } public static async void Task8() { await Task.Run(() => { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 8..."); Thread.Sleep(1000); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 8!"); }); }
期望输出
11:25:26 AM : Started task 1... 11:25:30 AM : Completed task 1! 11:25:30 AM : Started task 2... 11:25:32 AM : Completed task 2! 11:25:32 AM : Started task 3... 11:25:37 AM : Completed task 3! 11:25:37 AM : Started task 4... 11:25:38 AM : Completed task 4! ------------------ 11:25:38 AM : Task 5 starting... 11:25:38 AM : Task 6 starting... 11:25:38 AM : Task 7 starting... 11:25:38 AM : Task 8 starting... 11:25:39 AM : Task 8 completed! 11:25:40 AM : Task 6 completed! 11:25:42 AM : Task 5 completed! 11:25:43 AM : Task 7 completed!
原因分析
- 程序进程提前终止:Main方法是同步执行逻辑,调用完所有同步任务和异步任务后会直接结束进程。异步任务(Task5-Task8)运行在后台线程中,进程结束时后台线程会被强制终止,导致异步任务还未完成就被截断。
- async void的设计局限性:
async void定义的异步方法无法被调用者追踪和等待,Main方法无法获取这些异步任务的完成状态,自然无法等待它们执行完毕。async void仅适用于UI事件处理等特殊场景,普通异步方法应使用async Task作为返回类型。
解决方案
步骤1:修改异步方法的返回类型
将所有async void方法改为async Task,让调用者可以获取任务对象并等待其完成:
public static async Task Task5() { await Task.Run(() => { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 5..."); Thread.Sleep(4000); Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 5!"); }); } // Task6-Task8做同样修改,将async void替换为async Task
步骤2:修改Main方法以等待异步任务完成
从C# 7.1开始,Main方法可以定义为async Task类型,直接使用await等待所有异步任务完成:
public static async Task Main(string[] args) { // 同步任务按顺序执行 Task1(); Task2(); Task3(); Task4(); Console.WriteLine("------------------"); // 启动所有异步任务并等待全部完成 await Task.WhenAll(Task5(), Task6(), Task7(), Task8()); }
若使用低于C#7.1的版本,可在Main中调用Task.WhenAll(...).Wait()阻塞等待:
public static void Main(string[] args) { Task1(); Task2(); Task3(); Task4(); Console.WriteLine("------------------"); // 等待所有异步任务完成 Task.WhenAll(Task5(), Task6(), Task7(), Task8()).Wait(); }
额外优化建议
- 同步任务中的
Task.Delay(xxx).Wait()是阻塞调用,会占用主线程,若无需严格同步执行,可改为异步方式提升程序响应性。 - 异步任务中的
Thread.Sleep(xxx)会阻塞线程池线程,建议替换为await Task.Delay(xxx),避免浪费线程资源:
public static async Task Task5() { Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Started task 5..."); await Task.Delay(4000); // 替换Thread.Sleep Console.WriteLine(DateTime.Now.ToLongTimeString() + " : " + "Completed task 5!"); }
内容的提问来源于stack exchange,提问作者Showyeab Ahmed
相关产品推荐
相关产品推荐

