[英]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) Wait
或await
返回的Task
或嘗試訪問其Result
屬性,該異常就不會(重新)拋出在try
塊內。 (1)我希望我的英語會更好,找到更好更准確的描述。 正如Servy所指出的,在您的示例中,已經存在故障的Task
被返回。
在try
塊中引發異常時,將執行catch
塊。 當您寫return DoWorkThatMightThrowExceptionWithStateMachine();
try塊所做的所有事情都是構造一個標記為故障的Task
並將其返回。 不會拋出異常,因此不會運行catch
塊。
如果您等待失敗的任務,它將重新引發該異常,因此將引發異常。 當您調用DoWorkThatMightThrowExceptionWithoutStateMachine
,方法本身將引發異常,而不是返回錯誤的任務,因此try
塊中將引發異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.