簡體   English   中英

調用Task.Result時出現ThreadAbortException

[英]ThreadAbortException when calling Task.Result

我有以下代碼,我正在嘗試使用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;
   }
 }

..偶爾我會在readResponseTask.Result得到ThreadAbortExceptionreadResponseTask.Result所示:

System.Threading.ThreadAbortException:線程正在中止。 at System.Threading.Monitor.ObjWait(Boolean exitContext,Int32 millisecondsTimeout,Object obj)at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout,CancellationToken cancellationToken)at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout,CancellationToken cancellationToken)在System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout,CancellationToken cancellationToken)處於System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout,CancellationToken cancellationToken)

在什么情況下.Result拋出這樣的異常? 我已經嘗試在遠程端點上模擬超時但我在.Wait()而不是.Result得到了異常。 由於異常發生在.Wait() ,我假設結果已經從遠程站點返回但是在嘗試訪問結果時出現了某些問題。

有線索嗎? 它可能與線程並發有關嗎?

我會在readResponseTask.Result上得到ThreadAbortException

不,你沒有。 調用堆棧清楚地表明它實際上是產生異常的Wait()調用。 請注意跟蹤中經常出現“等待”一詞。

很難看出你是如何困惑的。 請記住,Task.Result屬性getter 非常小 ,當您運行程序的Release版本時,它將被內聯。 所以你永遠不能在堆棧跟蹤中看到它。

也許只需刪除Wait()調用就可以領先。 沒必要,Result屬性getter已經在必要時執行等待。

在Wait()期間,線程從外部中止。 沒人知道為什么。

啟用網絡客戶端跟蹤可以幫助檢測根本原因。

首先,如果要立即調用.Wait() ,請不要使用.*Async() .Wait() 這是不好的做法,很可能會導致錯誤的結果。 而是使用調用的同步版本client.Get(relativeUri)和以下內容:

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

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

但是,如果您希望利用異步I / O功能,則應遵循最佳實踐。 使用async / await關鍵字 ,並使您的方法看起來好像是同步的,同時利用.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()並導致此異常。 沒有什么可以阻止它。 無論如何,請嘗試遵循最佳實踐。

我在通過Unit-Tests測試方法時遇到了同樣的問題,該方法是將數據發送到WebService。 (我有更好的方法來做到這一點)

問題是單元測試稱為我的異步方法,但沒有等待單元,WebService調用已經完成。 這中止了WebService調用,我得到了一個ThreadAbortException

[TestMethod]
public void WebServiceTest01(){
    BusinessLogic bs = new BusinessLogic();
    bs.CallWebService();
}

我需要做的就是通過添加關鍵字await使Unit-Test等到WebService調用完成。

[TestMethod]
public void WebServiceTest01(){
    BusinessLogic bs = new BusinessLogic();
    var result = await bs.CallWebService();
    Assert.NotNull(result);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM