调用Task.Result

时间:2015-11-13 07:41:47

标签: c# multithreading thread-safety asp.net-web-api

我有以下代码,我尝试使用HttpClient向远程终端发出请求:

using (var client = new HttpClient())
{
   client.BaseAddress = _serviceBaseAddress;

   Task<HttpResponseMessage> readResponseTask = client.GetAsync(relativeUri);
   readResponseTask.Wait();

   using (var response = readResponseTask.Result)
   {
     if (response.StatusCode == HttpStatusCode.NotFound || !response.IsSuccessStatusCode)
     {
       return default(TResult);
     }

     Task<TResult> readContentTask = response.Content.ReadAsAsync<TResult>();
     readContentTask.Wait();

     TResult value = readContentTask.Result;

     return value;
   }
 }

..偶尔我会在ThreadAbortException得到readResponseTask.Result,如此:

  

System.Threading.ThreadAbortException:线程正在中止。在   System.Threading.Monitor.ObjWait(布尔值exitContext,Int32   millisecondsTimeout,Object obj)at   System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout,   取消语音取消语言)   System.Threading.Tasks.Task.SpinThenBlockingWait(的Int32   millisecondsTimeout,CancellationToken cancellationToken)at   System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout,   取消语音取消语言)   System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout,   取消语音取消语言)

.Result会在什么情况下抛出这样的例外?我已尝试模拟远程端点上的超时,但我在.Wait()而不是.Result处获得了异常。由于异常发生在.Wait()之后,我假设结果已经从远程站点返回,但在尝试访问结果时出现了某些问题。

任何线索?它可能与线程并发有关吗?

4 个答案:

答案 0 :(得分:6)

  

我会在readResponseTask.Result

中得到ThreadAbortException

不,你没有。调用堆栈清楚地表明它实际上是产生异常的Wait()调用。注意跟踪中经常出现“等待”一词。

很难看出你是如何感到困惑的。请记住,Task.Result属性getter是very small,当您运行程序的Release版本时,它将被内联。所以你永远可以在堆栈跟踪中看到它。

也许只需删除Wait()调用就可以领先。没必要,Result属性getter已经在必要时执行等待。

答案 1 :(得分:3)

在Wait()期间,线程从外部中止。没有人能确定原因。

启用network client trace有助于检测根本原因。

答案 2 :(得分:2)

首先,如果您要立即致电.*Async(),请不要使用.Wait()。这是不好的做法,很可能会导致错误的结果。而是使用调用的同步版本client.Get(relativeUri)和以下内容:

TResult value = response.Content.ReadAs<TResult>();    
return value;

如果您打算不利用.NET框架异步编程模型。

但是,如果您希望利用异步I / O功能,则应遵循最佳实践。使用async/await keywords并使您的方法看起来好像是同步的,同时利用.NET框架的异步关键字的功能。

我只能想象您的方法的入口点如下所示:

public TResult InvokeClientGet<TResult>(string relativeUri) 
{  
    // ... left out for brevity
}

这实际上阻止您使用async / await关键字。而是尝试以下方法:

    public async Task<TResult> InvokeClientGet<TResult>(string relativeUri)
    {
        try
        {
            using (var client = new HttpClient { BaseAddress = _serviceBaseAddress })
            {
                using (var response = await client.GetAsync(relativeUri))
                {
                    if (response.StatusCode == HttpStatusCode.NotFound || 
                        !response.IsSuccessStatusCode)
                    {
                        return default(TResult);
                    }

                    return await response.Content.ReadAsAsync<TResult>();
            }
        }
        catch (Exception ex)
        {
            // Handle exceptional conditions
        }
    }

专门针对System.Threading.ThreadAbortException的几句话。

  

当调用Abort方法来销毁线程时,公共语言运行库会抛出ThreadAbortException。 ThreadAbortException是一个可以捕获的特殊异常,但它会在catch块的末尾自动再次引发。引发此异常时,运行时会在结束线程之前执行所有finally块。因为线程可以在finally块中执行无限制计算或调用Thread.ResetAbort来取消中止,所以无法保证线程将永远结束。如果要等到中止的线程结束,可以调用Thread.Join方法。 Join是一个阻塞调用,在线程实际停止执行之前不会返回。

话虽如此,如果某事正在调用.Abort()并导致此异常。没有什么可以阻止它。无论如何,请尝试遵循最佳实践。

答案 3 :(得分:0)

我在通过Unit-Tests测试方法时遇到了同样的问题,该方法是将数据发送到WebService。 (我有更好的方法可以做到这一点)

问题是Unit-Test调用了我的异步方法,但没有等待unitService WebService调用完成。这中止了WebService调用,我得到了value

ThreadAbortException

我需要做的就是通过添加关键字[TestMethod] public void WebServiceTest01(){ BusinessLogic bs = new BusinessLogic(); bs.CallWebService(); } 使单元测试等到WebService调用完成。

await