【问题标题】:Async and Await in nested call. Not awaiting on a method which does an await causing IIS crash嵌套调用中的异步和等待。不等待执行等待导致 IIS 崩溃的方法
【发布时间】:2013-04-16 04:07:53
【问题描述】:

我一直在网上寻找一些问题,希望这里有人可以为我解答。我正在尝试解决导致 IIS 崩溃的问题。此服务调用应该是一种即发即忘的调用类型。如果失败,我们会抑制并记录异常。因为它是一个长时间运行的服务调用,所以它被实现为异步的。当前的实现有几个嵌套的方法调用如下:

// in first service
public void PerformService(some params)
{
    // setup and validation

    DoAction();
}
private void DoAction()
{
    // final setup

    OtherService.FireAndForgetAsync(some params);
}

// in the other service (OtherService)
public async Task FireAndForgetAsync()
{
    await Task.Run(() => 
    {
        try 
        {
           // do some long running stuff
        }
        catch (Exception e)
        {
            ErrorLogger.LogError(e)
        }
    }
}

实际上,我们处于一个循环(从 0 次迭代到多次迭代)调用 PerformService 并且当前遇到以下异常:

System.NullReferenceException: Object reference not set to an instance of an object. at   
System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonati onContext) at 
System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationCo ntext) at
System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state) at System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallb ack callback, Object state) at System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback call back, Object state) at System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAct ion(Object state) at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback c allback, Object

即使我在 try catch 块中强制发生异常(以测试我们的错误记录器),它也会被我们的错误记录器正确捕获和记录。请求完成后,似乎某些线程仍在尝试执行 SynchronizationContext.Post。我认为我们不正确地使用了 C# 5 的 async/await,因为 FireAndForgetAsync 正在等待,但没有别的。所以,我们说我们要等待,然后离开。 http请求结束,到那时它无法同步自己备份,因为没有任何东西可以同步。不好,因为这完全崩溃了 IIS。我主要是在寻找其他人的意见,因为我对 C# 5 中的 async/await 以及正确的解决方案不是很熟悉。由于我们不想等待它完成,因此从 FireAndForgetAsync 方法中删除 await 和 async 修饰符似乎应该可以解决问题。

【问题讨论】:

    标签: c# async-await c#-5.0


    【解决方案1】:

    此服务调用应该是一种即发即忘的调用类型。

    希望你没有任何重要的代码,因为IIS wasn't designed to be used like this

    [堆栈跟踪] ... LegacyAspNetSynchronizationContext ...

    红旗! LegacyAspNetSynchronizationContext 不能与 asyncawait 完美搭配。

    请确保您的 web.config 中有以下设置:

    <appSettings>
       <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    </appSettings>
    

    如果您在FireAndForgetAsync 中执行实际的业务逻辑,那么您应该更改您的架构:让DoAction 将工作发布到持久存储(例如,Azure 队列)并拥有独立的非 IIS 服务(例如, Azure 辅助角色)处理队列中的工作。

    如果您的FireAndForgetAsync 工作不重要,那么您可以使用BackgroundTaskManager type on my blog 之类的东西来缓解这种方法的问题 ...

    【讨论】:

      【解决方案2】:

      您不应该执行await,除非您在Task.Run lambda 完成后有事情要做。通过执行await,您实际上是在请求恢复上下文以在返回的任务完成之前基本上执行无操作。

      如果您有理由执行await(即在Task.Run lambda 完成后应该完成的一些工作),并且您不希望它尝试恢复上下文,那么您可以在从Task.Run 返回的任务上使用ConfigureAwait(false)

      查看这篇文章以获得很好的解释:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-31
        • 2021-06-30
        • 1970-01-01
        • 2016-07-07
        • 2016-03-25
        • 2017-10-09
        相关资源
        最近更新 更多