async/await 抛出 NullReferenceException 我们如何诊断我们在哪里搞砸了?

Posted

技术标签:

【中文标题】async/await 抛出 NullReferenceException 我们如何诊断我们在哪里搞砸了?【英文标题】:async/await throws NullReferenceException how can we diagnose where we messed it up? 【发布时间】:2016-11-02 07:29:52 【问题描述】:

我们已经开始在 asp.net 应用程序中使用 async/await,现在我们在生产中遇到了著名的异常

发生未处理的异常,进程终止。

应用程序 ID:/LM/W3SVC/376/ROOT

进程 ID:3796

异常:System.NullReferenceException

消息:对象引用未设置为对象的实例。

堆栈跟踪:在 System.Web.ThreadContext.AssociateWithCurrentThread(布尔 setImpersonationContext) 在 System.Web.HttpApplication.OnThreadEnterPrivate(布尔值 setImpersonationContext) 在 System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback 回调,对象状态)在 System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback 回调,对象状态)在 System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback 回调,对象状态,任务和当前任务) --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Threading.Tasks.AwaitTaskContinuation.b__1(对象 s) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch()

有什么方法可以让我们获得有关产生问题的代码/任务的更多信息?

第二个问题:我们试图在一个简单的测试 webform 应用程序中本地重现异常

        protected void Page_Load(object sender, EventArgs e)
    
        LogMessageToFile("before_task");
        var t = Test();

        tasks.Add(t);
    
    async Task Test()
    
        await Task.Run(() =>
       
           LogMessageToFile("inside_task");
           Thread.Sleep(1000);
       
            );
        this.Title = "test";
        LogMessageToFile("after_task");

        //  throw new Exception("");
    

但是我们的测试页面从来没有出现异常,好像Test函数中await之后的代码从来没有被调用,并且任务状态是WaitingForActivation,为什么我们在这段代码中没有出现异常?

【问题讨论】:

try catch block araound await? this.Title = "test"; 可以为 null 如果页面已经消失并且您不关心...在页面消失之前等待。 【参考方案1】:

调用堆栈中的旧类型 (LegacyAspNetSynchronizationContext) 表明您的 web.config settings 不正确。将targetFramework 设置为4.5 或更高。

async/await 在早期版本的 ASP.NET 上导致未定义的行为。

为什么我们在这段代码中没有出现异常?

因为您可能更新损坏的应用程序到 4.5+(打开“怪癖模式”,呈现 await 不可用),但创建了一个 测试应用程序4.5+(关闭“怪癖模式”,允许await 工作)。

【讨论】:

感谢 Stephan 的帮助,我仔细检查了 web.config 我们在生产中有 但没有 关于问题中的示例代码,我们可以说,当我们使用旧配置执行 await (this.Title = "test"; LogMessageToFile("after_task");) 任务后的代码时想要重新加入主上下文并引发异常(bcz 它不存在)但是使用新配置,任务将在没有完成和重新加入的情况下被放弃,并且代码永远不会被调用?这意味着我们的代码中可能仍然存在某种错误,我们没有等待任务完成,因此之后的代码可能永远不会被调用,使用旧配置我们会收到错误,但使用新配置我们不会收到错误? @Mojtaba:我发现示例代码存在两个问题。 1) 任务最终应该是 awaited,并且 2) Task.Run 不适合 ASP.NET 应用程序。 如果我收到此错误并且没有看到 LegacyAspNetSynchonizationContext? :-( @Simon_Weaver:那么问题可能出在您的代码中,而不是运行时,并且可能与async/await 无关。如果不清楚问题出在哪里,请创建一个重现问题的最小示例并将代码发布到您自己的问题中。

以上是关于async/await 抛出 NullReferenceException 我们如何诊断我们在哪里搞砸了?的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 webpack-simple 配置的 async/await 抛出错误:RegeneratorRuntime 未定义

使用 Async Await 模式的 EF 核心已经抛出打开的 DataReader 错误

嵌套的 try、catch 和 async、await 请求

使用 async/await 锁定资源

使用Async / await和mongoose

Swift新async/await并发模型中子任务取消不能被其它子任务感知的原因及解决