C#中異步編程異常的處理方式


異步編程異常處理

 在同步編程中,一旦出現錯誤就會拋出異常,我們可以使用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();
        }
方法其實和上面的判斷狀態再去獲取異常信息差不多,該方法主要是在日志中使用較多。

如有哪里講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM