C#异步代码异常:两段Main方法请求服务端分别偶发失败和卡死
两段代码问题原因解析
第一段 async void Main 偶发请求失败原因
async void方法没有返回可被跟踪的Task对象,运行时无法等待这类方法执行完成- 作为程序入口的
Main方法被标记为async void时,代码执行到await GetAsync行就会立刻将控制权交回给程序启动的运行时,运行时会判定Main已经执行完毕,直接启动进程退出流程 - 只有在进程完全终止前,网络请求刚好完成全部逻辑,才会表现为请求成功,否则请求会被中途中断,所以会出现有时成功有时失败的现象
注:C# 7.1 及以上版本已经支持async Task Main的入口写法,专门用于解决异步入口的等待问题
第二段 .Wait() 无限挂起原因
这是典型的异步同步上下文死锁问题,触发流程如下:
- 默认情况下(桌面应用、.NET Framework ASP.NET 场景),异步方法执行时会捕获当前的同步上下文,这类同步上下文是单线程独占的,同一时间只能有一个操作在上下文上执行
- 调用
.Wait()时会直接阻塞当前主线程,等待GetAsync返回的Task执行完成 GetAsync的网络IO请求完成后,需要回到之前捕获的同步上下文执行后续收尾逻辑,但此时主线程已经被.Wait()阻塞,无法释放上下文给异步任务使用,就形成了「主线程等待Task完成,Task等待主线程释放上下文」的循环死锁,程序会永久挂起,请求自然也无法正常完成
临时规避可改成new HttpClient().GetAsync("http://localserver").ConfigureAwait(false).Wait(),但更推荐全程使用await做异步等待,不要混用同步阻塞方法
内容的提问来源于stack exchange,提问作者KittyDotNet
相关产品推荐
相关产品推荐

