引言
現(xiàn)在模擬一個異步方法拋出了異常:
public static async Task ThrowAfter(int ms, string message)
{
await Task.Delay(ms);
throw new Exception(message);
}
思考一下, DontHandle()
方法是否能夠捕獲到異常?
public static void DontHandle()
{
try
{
ThrowAfter(1000, "first");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
答案是:不會捕獲到異常!
因為 DontHandle()
方法在 ThrowAfter()
方法拋出異常之前,就已經(jīng)執(zhí)行完畢。
異步方法的異常處理
那么上述代碼怎么才能捕獲到異常呢?
若想要捕獲異常則必須通過 await
關(guān)鍵字等待 ThrowAfter()
方法執(zhí)行完成。
將上文中的代碼段進行修改:
public static async void HandleoOnError()
{
try
{
await ThrowAfter(1000, "first");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
結(jié)果就會輸出:
first
多個異步方法的異常處理
如果調(diào)用兩個異步方法,每個都會拋出異常,該如何處理呢?
我們可以這樣寫:
public static async void StartTwoTasks()
{
try
{
await ThrowAfter(1000, "first");
await ThrowAfter(1000, "second");
Console.WriteLine("StartTwoTasks is Complate");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
思考一下輸出是什么?
答案是:
first
并沒有預(yù)想中的兩個異常都捕獲打印出來,也沒有看到“StartTwoTasks is Complate”這句話打印出來。因為使用 await
關(guān)鍵字之后,兩次調(diào)用 ThrowAfter()
方法就變成了同步執(zhí)行,捕獲到第一次的異常之后直接進入到 catch
代碼段,不再執(zhí)行后續(xù)代碼。
可以嘗試解決這個問題,使用 Task.WhenAll()
方法,該方法不管任務(wù)是否拋出異常,都會等到兩個任務(wù)完成。如下代碼:
public static async void StartTwoTasksParallel()
{
try
{
Task t1 = ThrowAfter(1000, "first");
Console.WriteLine("t1 is Complate");
Task t2 = ThrowAfter(1000, "second");
Console.WriteLine("t2 is Complate");
await Task.WhenAll(t2, t1);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
輸出:
t1 is Complate
t2 is Complate
second
從輸出可以看出來,使用 WhenAll()
方法,兩個任務(wù)都是執(zhí)行完成的,但是,捕獲異常只能捕獲 WhenAll()
方法參數(shù)中,排在最前面的,且第一個拋出異常的任務(wù)的消息,
上述方式有缺陷,只能拋出一個異常的任務(wù)的消息,可以將上面的方式再進化一下,如下代碼:
public static async void StartTwoTasksParallelEx()
{
Task t1 = null;
Task t2 = null;
try
{
t1 = ThrowAfter(1000, "first");
t2 = ThrowAfter(1000, "second");
await Task.WhenAll(t2, t1);
}
catch (Exception ex)
{
if (t1.IsFaulted)
{
Console.WriteLine(t1.Exception.InnerException.Message);
}
if (t2.IsFaulted)
{
Console.WriteLine(t2.Exception.InnerException.Message);
}
}
}
輸出:
first
second
在 try/catch
代碼塊外聲明任務(wù)變量t1、t2,使他們可以在 try/catch
塊內(nèi)訪問,在這里,使用了IsFaulted
屬性,檢查任務(wù)的狀態(tài),若IsFaulted
屬性為 true
,則表示該任務(wù)出現(xiàn)異常,就可以使用 Task.Exception.InnerException
訪問異常本身。
使用AggregateException信息
除了上述方式外,還有一種更好的獲取所有任務(wù)的異常信息的方式,Task.WhenAll()
方法返回的結(jié)果其實也是一個 Task
對象,而 Task
有一個 Exception
屬性,它的類型是 AggregateException
,是 Exception
的一個派生類,AggregateException
類有一個 InnerExceptions
屬性(異常集合,包含 Task.WhenAll()
方法列表中所有異常任務(wù)的異常信息)。
有了這個屬性則可以輕松遍歷所有異常。如下代碼:
public static async void StartTwoTasksParallelEx2()
{
Task t3 = null;
try
{
Task t1 = ThrowAfter(1000, "first");
Task t2 = ThrowAfter(1000, "second");
await (t3 = Task.WhenAll(t2, t1));
}
catch (Exception ex)
{
foreach (var item in t3.Exception.InnerExceptions)
{
Console.WriteLine("InnerException:" + item.Message);
}
}
}
輸出:
InnerException:second
InnerException:first
總結(jié)
除了前面提到的異步方法異常處理的基本知識點,以下是一些進階的異常處理技巧:
-
在異步方法中,如果需要將異常傳遞給調(diào)用方,請不要直接拋出異常。相反,應(yīng)該使用 throw 關(guān)鍵字將異常包裝在一個
Task
或ValueTask
對象中,并將其返回給調(diào)用方。這可以避免在異步操作中丟失異常信息。 -
如果需要在異步方法中處理多個異常,可以使用
catch
塊來捕獲不同類型的異常,并根據(jù)需要執(zhí)行不同的處理操作。還可以使用finally
塊來執(zhí)行清理操作,例如釋放資源或恢復(fù)狀態(tài)。 -
如果需要在異步方法中執(zhí)行一些異步操作,并且這些操作都必須成功才能繼續(xù)執(zhí)行下一步操作,那么可以使用
Task.WhenAll
方法來等待所有異步操作完成。如果任何一個異步操作失敗,WhenAll
方法將返回一個AggregateException
對象,其中包含所有失敗的異常。 -
如果需要在異步方法中執(zhí)行多個異步操作,并且這些操作中的任何一個失敗都將導(dǎo)致整個操作失敗,那么可以使用
Task.WhenAny
方法來等待第一個異步操作完成。如果第一個操作失敗,WhenAny
方法將返回一個AggregateException
對象,其中包含第一個失敗的異常。 -
如果需要在異步方法中進行錯誤處理并且希望能夠獲取更多有關(guān)異常的信息,可以使用
ExceptionDispatchInfo
類。這個類可以捕獲異常并將其存儲在一個對象中,然后在需要時重新拋出異常。這可以幫助在異步操作中保留異常信息,并將其傳遞給調(diào)用方。文章來源:http://www.zghlxwxcb.cn/news/detail-617086.html
總之,在異步方法中處理異常時,需要注意一些細節(jié)和技巧,例如正確處理異常、捕獲多個異常、等待多個異步操作、以及使用 ExceptionDispatchInfo
類來捕獲異常。掌握這些處理技巧可以幫助編寫更可靠、更健壯的異步代碼。文章來源地址http://www.zghlxwxcb.cn/news/detail-617086.html
到了這里,關(guān)于并發(fā)編程 --- 異步方法的異常處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!