ASP.NET 谨用 async/await

2019-05-25 14:58:13王旭

针对死锁问题的解决方式是增加 ConfigureAwait(false)

// await Task.Delay(1000);
await Task.Delay(1000).ConfigureAwait(false); // 解决死锁

当 await 等待完成时,它会尝试在线程池上下文中执行 async 方法的剩余部分,因此就不存在死锁。

应用程序崩溃

测试环境总发现IIS应用程序池总是崩溃,到底是什么原因?当时我们对这个问题也是非常懵逼,代码看上去并没什么明显毛病,试图欺骗自己应该是环境本身的问题吧,但事实上确实是在代码中下了毒。通过各种资料查阅和测试,基本可以断定是同步代码调用异步代码,同步上下文引起的问题。

如果调用一个 async 方法。如果使用 await 等待,当前线程立马被释放回线程池,线程的上下文信息会被保存。如果没有使用 await(async void 的方法,必然没有办法使用 await),调用 async 方法之后,代码会继续往下执行,执行完成后当前线程被释放回线程池,线程的上下文信息不会被保存。当 async 中的异步任务执行完成后,会从线程池中获取一个线程继续执行剩余代码,同时会获取当初调用者所在线程的上下文信息(如果当初调用者所在线程没有释放回线程池,上下文信息可以获取到)。那么问题就来了,如果当初调用者没有使用 await 并且 所在线程释放回线程池了,上下文信息因为没有被保持下来,就获取不到了,这时候会抛出异常 未将对象引用设置到对象的实例 ,经过测试这个异常信息并不一定每次都会出现,原因和线程的释放有关,调用者所在线程的上下文信息存在就不会抛出异常。异常错误信息如下,这个异常最终会导致应用程序集停止。

在 System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
  在 System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
  在 System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
  在 System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
  在 System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state)
  在 System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state)
  在 System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
  在 System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s)
  在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
  在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
  在 System.Threading.ThreadPoolWorkQueue.Dispatch()
  在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()