C#同步方法中调用未等待异步方法的执行异常问题咨询
问题根因
这是即发即弃调用异步方法的典型问题:当你没有通过await、Wait()等方式等待异步Task执行完成时,一旦外层同步上下文结束(控制台程序中是主线程执行完毕、MVC场景中是请求处理完成),未完成的异步任务会被直接终止,自然不会执行后续代码。
示例代码执行流程拆解
你提供的代码执行顺序如下:
internal async Task<int> Method1Async() { //await Task.Delay(1000); Console.WriteLine("Inside Method 1 : Before Calling await"); await Task.FromResult(5); //await Task.Delay(1000); Console.WriteLine("Inside Method 1 : After Calling await"); return await Task.FromResult(1); } internal async Task<int> Method2Async() { //await Task.Delay(1000); Console.WriteLine("Inside Method 2 : Before Calling await"); var res = await Method1Async(); await Task.Delay(1000); Console.WriteLine("Inside Method 2 : After Calling await"); return res; } internal async Task<int> Method3Async() { //await Task.Delay(1000); Console.WriteLine("Inside Method 3 : Before Calling await"); var res = await Method2Async(); await Task.Delay(1000); Console.WriteLine("Inside Method 3 : After Calling await"); return res; } internal void Method4() { Method3Async(); } public static void Main() { Console.WriteLine("Inside Main : Before calling Method4"); var demo = new Demo(); demo.Method4(); Console.WriteLine("Inside Main : After calling Method4"); }
- 主线程进入Main方法,输出
Inside Main : Before calling Method4,调用Method4 - Method4调用Method3Async,异步方法在遇到第一个未完成的await之前,所有代码都是同步在当前线程执行:
- Method3Async输出
Inside Method 3 : Before Calling await,调用Method2Async - Method2Async输出
Inside Method 2 : Before Calling await,调用Method1Async - Method1Async输出
Inside Method 1 : Before Calling await,await Task.FromResult(5)是已完成的Task,不需要挂起,继续同步输出Inside Method 1 : After Calling await,返回结果给Method2Async
- Method3Async输出
- Method2Async接下来遇到
await Task.Delay(1000),这是未完成的Task,当前线程会被释放返回上层,Method2Async、Method3Async依次返回未完成的Task给Method4,Method4没有等待Task直接返回 - 主线程继续执行输出
Inside Main : After calling Method4,Main方法执行完毕,整个控制台进程开始退出,所有后台线程被直接终止,Task.Delay之后的代码没有执行机会,也就不会有后续输出。
MVC场景的额外注意事项
同步Action执行完成后,ASP.NET运行时会直接标记请求处理完成,释放请求上下文,此时未完成的异步任务不仅可能被直接终止,就算后续继续执行,也可能因为上下文已经被释放抛出异常,未被观测的异常甚至会导致进程崩溃,完全不推荐在MVC中直接使用即发即弃的异步调用。
解决方案
- 如果你需要等异步方法执行完成再返回结果:将同步Action改为异步Action,用
await等待异步方法执行;如果必须保持Action同步,可调用异步方法的.GetAwaiter().GetResult()获取结果,注意异步方法内部所有await都要加.ConfigureAwait(false)避免上下文捕获引发死锁。 - 如果你确实不需要等待结果,需要后台执行任务:不要直接写即发即弃调用,使用专门的后台任务方案,比如ASP.NET Framework的
HostingEnvironment.QueueBackgroundWorkItem、ASP.NET Core的IHostedService,或者用Hangfire、消息队列等第三方任务调度方案,确保任务不会因为请求结束被销毁。
内容的提问来源于stack exchange,提问作者Ravindra Sinare
相关产品推荐
相关产品推荐

