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

Ber*_*rdG 12 c# async-await

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

    private async Task<XElement> 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<XElement>();
            response.Dispose();
            client.Dispose();
            return content;
        }
        catch (Exception)
        {
            return null;
        }
    }
Run Code Online (Sandbox Code Playgroud)

没有什么花哨的,它确实运作良好....但是,现在,在一个特定的电话,它炸弹client.GetAsync(url).起初我怀疑网址中的某些内容是错误的,所以我从调试器会话中抓取它并将其直接粘贴到我的浏览器中,得到了预期的答案...

所以,URL没有错.做了一个小单元测试,使用相同的特定URL工作得很好......

因为它在调试器中炸弹,很难看出什么是错的.(没有抛出异常!).最后,我看到IntelliTrace中有异常,似乎在里面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#
Run Code Online (Sandbox Code Playgroud)

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

感谢你的帮助,伯纳德.

Chr*_*ini 24

再加上@ 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();
}
Run Code Online (Sandbox Code Playgroud)

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

这很难诊断.在生产中,你不会在try/catch中看到任何东西,并且在等待计划重新加入原始上下文的Visual Studio中有些随机 - 这取决于TaskScheduler碰巧决定做什么.

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

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


小智 5

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