異步編程異常處理
在同步編程中,一旦出現錯誤就會拋出異常,我們可以使用try…catch來捕捉異常,未被捕獲的異常則會不斷向上傳遞,形成一個簡單而統一的錯誤處理機制。但是對於異步編程來說,異常處理一直是件麻煩的事情,所以接下來給大家介紹一下異步編程中的錯誤處理方式
單個異常的捕獲
public static async Task ThrowExcrptionAsync(int ms, string message)
{
await Task.Delay(ms);
throw new Exception(message);
}
public static async Task Main(string[] args)
{
try
{
ThrowExcrptionAsync(2000, "first");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
如果調用以上的方法,並且沒有等待,可以將異步方法放在try/catch中就可以捕獲到異常,比如像上面一樣調用ThrowExcrptionAsync方法,方法已經執行完畢,而throw new Exception(message)這句話還沒執行,所以上面這段代碼並不會捕獲到異常
注意:返回void的異步方法不會等待,這是因為從async void方法拋出的異常無法捕獲,因此,異步方法最好返回一個Task類型。處理程序方法或重寫的基類方法不受此規則限制
異步方法異常的一個比較好的處理方式是使用await關鍵字,將其放在try/catch語句中,如以下代碼瑣事。異步調用ThrowExcrptionAsync方法后,主線程就會釋放線程,但塔會在任務完成時保持任務的引用,此時(2s之后)會調用匹配的catch塊內的代碼
public static async Task Main(string[] args)
{
try
{
await ThrowExcrptionAsync(2000, "first");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
多個異常的捕獲
此時如果調用兩個異步方法,每個方法都會拋出異常,我們該如何處理呢?如以下代碼
public static async Task Main(string[] args)
{
try
{
await ThrowExcrptionAsync(2000, "first");
await ThrowExcrptionAsync(1000, "second");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
第一個ThrowExcrptionAsync被調用,2s拋出異常信息(包含信息first),該方法結束后,另一個ThrowExcrptionAsync方法也被調用,1s之后拋出異常,事實並非如此,因為第一個ThrowExcrptionAsync已經拋出了異常,try塊內的代碼塊並沒有繼續調用第二個ThrowExcrptionAsync方法。而是直接進入catch塊內的第一個異常進行處理,但是在現實編程中,這個並非我們所想。我們需要兩個方法不管異常,都需要執行,而不是某一個報錯直接跳出,所以我們使用WhenAll方法等待所有方法執行完成再catch(在外部定義兩個task對象,用來接受我們要執行方法的結果),
public static async Task Main(string[] args)
{
Task t1 = null;
Task t2 = null;
try
{
t1 = ThrowExcrptionAsync(2000, "first");
t2 = ThrowExcrptionAsync(1000, "second");
await Task.WhenAll(t1, t2);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
這個時候,我們已經讓兩個方法都執行了。沒有管內部的錯誤,接下來我們去捕獲兩個異常中的錯誤信息,代碼如下
public static async Task Main(string[] args)
{
Task t1 = null;
Task t2 = null;
try
{
t1 = ThrowExcrptionAsync(2000, "first");
t2 = ThrowExcrptionAsync(1000, "second");
await Task.WhenAll(t1, t2);
}
catch (Exception e)
{
if (t1.IsFaulted)
{
Console.WriteLine(t1.Exception.InnerException);
}
if (t2.IsFaulted)
{
Console.WriteLine(t2.Exception.InnerException);
}
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
在這里我們使用的的是IsFaulted屬性,該屬性用來檢查任務的狀態,以確定它們是否為出錯狀態,若出現異常,IsFaulted屬性會返回true。可以使用Task類中的Exception.InnerException來訪問信息本身。當然,我們知道IsFaulted的狀態后。肯定是可以進行別的業務邏輯處理的。
另外還有一種比較快速獲取task類中的異常信息的,代碼如下:
public static async Task Main(string[] args)
{
Task taskResult = null;
try
{
var t1 = ThrowExcrptionAsync(2000, "first");
var t2 = ThrowExcrptionAsync(1000, "second");
await (taskResult = Task.WhenAll(t1, t2));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
foreach (var item in taskResult.Exception.InnerExceptions)
{
Console.WriteLine(item.Message);
}
}
Console.ReadKey();
}
方法其實和上面的判斷狀態再去獲取異常信息差不多,該方法主要是在日志中使用較多。
如有哪里講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧