一. Task的各種返回值-Task<TResult>
PS: 在前面章節,我們介紹了Task類開啟線程、線程等待、線程延續的方式,但我們並沒有關注這些方式的返回值,其實他們都是有返回值的Task<TResult>,然后可以通過Task的實例調用Result屬性來獲取這個返回值。
下面我們分三類來介紹:
①:線程開啟類的返回值, 使用Task<TResult>接受,或者直接使用Task接受,通過 實例.Result 來獲取返回值。這里的線程開啟類有多種,eg: Task.Run()、 task.start()、 Task.Factory.StartNew() 等。
②:線程延續類的返回值. eg:ContinueWith。
③:線程條件延續類的返回值. eg:WhenAll和WhenAny。
1. 線程開啟類的返回值
1 { 2 Task<string> task1 = Task.Factory.StartNew(() => 3 { 4 Console.WriteLine("我是子線程哦"); 5 return "ok"; 6 }); 7 task1.Wait(); 8 Console.WriteLine("我是主線程,我要讀取子線程task1的返回值為:{0}", task1.Result); 9 }
2. 線程延續類的返回值
1 { 2 Task<int> task1 = Task.Run(() => 3 { 4 Console.WriteLine("我是子線程1哦"); 5 return 2; 6 }); 7 8 var task2 = task1.ContinueWith((t) => 9 { 10 Console.WriteLine("我是子線程2哦"); 11 12 //這里的t代表 task1 13 var num = t.Result + 2; 14 return num.ToString(); 15 }); 16 17 task2.Wait(); 18 Console.WriteLine("我是主線程,我要讀取子線程task1的返回值為:{0}", task1.Result); 19 Console.WriteLine("我是主線程,我要讀取子線程task2的返回值為:{0}", task2.Result); 20 }
3. 線程條件延續類
1 { 2 Task<int> task1 = Task.Run(() => 3 { 4 Console.WriteLine("我是子線程1哦"); 5 return 1; 6 }); 7 Task<int> task2 = Task.Run(() => 8 { 9 Console.WriteLine("我是子線程2哦"); 10 return 2; 11 }); 12 13 var task = Task.WhenAny(new Task<int>[2] { task1, task2 }); 14 task.Wait(); 15 16 //下面的值可能是1,也可能是2 17 Console.WriteLine("我是主線程,我要讀取子線程task的返回值為:{0}", task.Result.Result); 18 }
二. 通用線程異常處理方案
1. 背景:我們想達到一個目的,當同時開啟多個線程的時候,其中一個線程報錯,不影響其他線程的執行,並且能把錯誤記下來。
2. 解決方案:多重try-catch,整個外側主線程一個try-catch,然后線程執行業務再用一個try-catch包裹起來。
常規方式捕獲異常:
1 { 2 try 3 { 4 for (int i = 0; i < 5; i++) 5 { 6 string name = string.Format("name{0}", i); 7 var task = Task.Run(() => 8 { 9 try 10 { 11 //模擬某個線程出錯 12 if (name == "name2") 13 { 14 throw new Exception(string.Format("線程執行失敗,i={0}", name)); 15 } 16 else 17 { 18 Console.WriteLine(string.Format("線程執行執行成功,i={0}", name)); 19 } 20 } 21 catch (Exception ex) 22 { 23 Console.WriteLine(ex.Message); 24 } 25 26 }); 27 taskList.Add(task); 28 } 29 Task.WaitAll(taskList.ToArray()); 30 } 31 catch (Exception ex) 32 { 33 Console.WriteLine(ex.Message); 34 35 } 36 }
運行結果:我們發現所有的線程均執行完畢,且name2執行失敗,並捕獲。
補充一下:通過 AggregateException 類來捕獲異常。
1 { 2 try 3 { 4 for (int i = 0; i < 5; i++) 5 { 6 string name = string.Format("name{0}", i); 7 var task = Task.Run(() => 8 { 9 throw new Exception(string.Format("線程執行失敗,i={0}", name)); 10 }); 11 taskList.Add(task); 12 } 13 Task.WaitAll(taskList.ToArray()); 14 } 15 catch (AggregateException aes) 16 { 17 foreach (var item in aes.InnerExceptions) 18 { 19 Console.WriteLine(item.Message); 20 } 21 } 22 }