簡體   English   中英

令人驚訝的情況是,異步方法中的異常處理未捕獲到異常

[英]Surprising case where exception handling in async method does not catch the exception

我的代碼將異步方法與異常處理結合在一起。 代碼非常簡單,等待所有任務,並且沒有async void方法:

async Task DoWorkSafelyWithStateMachine()
{
    try
    {
        await DoWorkThatMightThrowException();
    }
    catch (Exception exception)
    {
        Console.WriteLine("With state machine: " + exception.Message);
    }
}

等待該方法不會引發異常,因為該異常已被吞下:

await DoWorkSafelyWithStateMachine(); // No exception thrown

但是,代碼並不總是像預期的那樣捕獲異常。 當方法以略有不同的方式編寫而編譯器沒有創建異步狀態機時,就會出現問題:

Task DoWorkSafelyWithoutStateMachine()
{
    try
    {
        return DoWorkThatMightThrowException();
    }
    catch (Exception exception)
    {
        Console.WriteLine("Without state machine: " + exception.Message);
        return Task.CompletedTask;
    }
}

該方法未使用async修飾,並且該方法內部未等待任何內容。 相反, try內部方法返回的任務將返回給調用方。 但是,以我的經驗,編譯器的魔力仍然可以確保如果try的方法引發異常,則它會被異常處理程序捕獲。 好吧,顯然這並不總是正確的。

為了測試同一方法的這兩種變體,我讓DoWorkThatMightThrowException拋出異常。 如果方法的主體中不必使用await ,則可以在有或沒有異步狀態機的情況下實現該方法:

async Task DoWorkThatMightThrowExceptionWithStateMachine()
{
    throw new Exception("With state machine");
    await Task.CompletedTask;
}

Task DoWorkThatMightThrowExceptionWithoutStateMachine()
{
    throw new Exception("Without state machine");
    return Task.CompletedTask;
}

我發現從DoWorkSafelyWithoutStateMachine調用DoWorkThatMightThrowExceptionWithStateMachine不會捕獲引發的異常。 其他三個組合確實捕獲了異常。 特別是,在這兩種方法中都沒有異步狀態機的版本會捕獲該異常,並且我錯誤地推斷了這種觀察結果,但不幸的結果是我的某些代碼現在有細微的錯誤。

| Throw + state machine | Throw - state machine |
--------------------------+-----------------------+-----------------------+
Try/catch + state machine |        Caught         |        Caught         |
Try/catch - state machine |      Not caught       |        Caught         |

通過做這個實驗,我了解到我總是必須在try塊(表中的第一行)中await任務。 但是,我不理解表第二行中的不一致。 請解釋這種現象。 它在哪里記錄? 搜索有關此信息並不容易。 由於未等待任務而導致的“丟失”異常的更基本問題將主導搜索結果。

我不理解表格第二行中的不一致

右側的情況(根本沒有狀態機)是微不足道的:

  • 您在try/catch塊中調用方法
  • 該方法引發異常
  • 你的catch黑了它-沒有什么特別的

左側實際上也很容易理解:

  • 您在try/catch塊中調用方法
  • 但是這個方法看起來並不像,它已經轉換為狀態機,所以它返回的是一個Task ,代表您在實現該方法時執行該方法的內容(1)
  • 因此只要您不Waitawait返回的Task或嘗試訪問其Result屬性,該異常就不會(重新)拋出在try塊內。

(1)我希望我的英語會更好,找到更好更准確的描述。 正如Servy所指出的,在您的示例中,已經存在故障的Task被返回。

try塊中引發異常時,將執行catch塊。 當您寫return DoWorkThatMightThrowExceptionWithStateMachine(); try塊所做的所有事情都是構造一個標記為故障的Task並將其返回。 不會拋出異常,因此不會運行catch塊。

如果您等待失敗的任務,它將重新引發該異常,因此將引發異常。 當您調用DoWorkThatMightThrowExceptionWithoutStateMachine ,方法本身將引發異常,而不是返回錯誤的任務,因此try塊中將引發異常。

暫無
暫無

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

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