System.Threading.Tasks中的NullReferenceException调用HttpClient.GetAsync(url)

我的MVC 4.0应用程序中有一个奇怪的问题。 我使用REST Web服务(Amazon Associate)。 我创建了一个方法,我从各处使用它。 缩短版本是这样的:

private async Task GetRequest(string url) { string myresponse; HttpResponseMessage response = null; HttpClient client = new HttpClient(); try { response = await client.GetAsync(url); myresponse = response.Content.ToString(); if (myresponse.Contains("503")) { myTrace.WriteLine("503 Sleep....."); Thread.Sleep(3000); // looks like amazon us does not like fast requests.... return await GetRequest(url); //restart after pausing.... } } catch (TaskCanceledException ex) { myTrace.WriteLine("TaskCancelled From GetRequest: " + ex); return null; } catch (HttpRequestException ex) { myTrace.WriteLine("RequestException Sleep....."); Thread.Sleep(300000); // 5 minutes de pause } catch (Exception ex) { myTrace.WriteLine("From GetRequest: " + ex); return null; } try { XElement content = await response.Content.ReadAsAsync(); response.Dispose(); client.Dispose(); return content; } catch (Exception) { return null; } } 

没什么好看的,它确实工作得很好……但是,现在,在特定的电话中,它会在客户client.GetAsync(url)client.GetAsync(url) 。 起初我怀疑url中的内容是错误的,所以我从调试器会话中抓取它并将其直接粘贴到我的浏览器中,得到了预期的答案……

所以,URL没有错。 做了一个小unit testing,使用相同的特定URL工作得很好……

因为它在调试器中炸弹,很难看出什么是错的。 (没有抛出exception!)。 最后,我看到IntelliTrace中有exception,似乎在System.Threading.Tasks 。 很难确定点,因为调用Stack对我的NON专家眼睛来说有点混乱….

这是我从代码中的上一个传递获得的调用堆栈:

 > System.Web.dll!System.Web.ThreadContext.AssociateWithCurrentThread(bool setImpersonationContext = {unknown}) C# System.Web.dll!System.Web.HttpApplication.OnThreadEnterPrivate(bool setImpersonationContext = {unknown}) C# System.Web.dll!System.Web.HttpApplication.OnThreadEnter() C# System.Web.dll!System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter() C# System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action = {unknown}) C# System.Web.dll!c__DisplayClass9.AnonymousMethod(System.Threading.Tasks.Task _ = {unknown}) C# mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() C# mscorlib.dll!System.Threading.Tasks.Task.Execute() C# mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj = {unknown}) C# mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C# mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C# mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot = {unknown}) C# mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution = {unknown}) C# mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() C# mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() C# mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() C# 

无论如何,这看起来肯定与任务,异步,后台工作者等相关联……是否有一种很好的方法来“清除”所有其他正在运行的任务,以避免这个问题?

感谢你的帮助,伯纳德。

再加上@ kcar的答案,我有一个非常类似的问题,在代码路径上有多个等待,有一个没有等待的方法,如:

 public async Task JsonResult BookThing(InputModel model) { // Do some stuff thisIsAnAsyncMethod(Model model); // Fire and forget return Json(null); } protected async Task thisIsAnAsyncMethod(Model model) { await oneThing(); await anotherThing(); await somethingElse(); } 

这导致等待随机失败而不让我捕获exception – 因为TPL试图重新加入已被淘汰的Context,所以它在try / catch之外抛出NullReferenceException。

这很难诊断。 在生产中你不会在try / catch中看到任何东西,并且在Visual Studio中await被调度重新加入原始上下文有点随机 – 这取决于TaskScheduler碰巧决定做什么。

如果您不想触发并忘记明显的答案是等待异步方法 – 您将有一个编译器警告提醒您这样做。

如果您确实想要解雇并忘记解决方案是明确启动一个新任务。 关于火灾和忘记任务的答案涵盖了这样做的最佳方式。

从它的外观来看,你的代码可以在其中一个线程在第一个try块的exception事件中进入睡眠状态之前完成,所以在它们唤醒5分钟后,没有一个原始线程重新加入导致来自AssociateWithCurrentThread的NullReferenceException

当我得到上面显示的exception和调用堆栈时,这是因为我试图使用异步执行“火灾并忘记”执行,这是一个非常糟糕的主意。 为了我想要的东西,我切换到新线程的旋转,崩溃消失了。