中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Task類使用總結

發布時間:2020-05-04 13:25:57 來源:網絡 閱讀:14230 作者:cnn237111 欄目:編程語言

由于Framework 4.0和Framework 4.5對Task類稍微有些不同,此處聲明以下代碼都是基于Framework 4.5


Task類和Task<TResult>類,后者是前者的泛型版本。TResult類型為Task所調用方法的返回值。

主要區別在于Task構造函數接受的參數是Action委托,而Task<TResult>接受的是Func<TResult>委托。

  1. Task(Action) 
  2. Task<TResult>(Func<TResult>)  

啟動一個任務 

  1. static void Main(string[] args) 
  2.         { 
  3.             Task Task1 = new Task(() => Console.WriteLine("Task1")); 
  4.             Task1.Start(); 
  5.             Console.ReadLine(); 
  6.         } 

通過實例化一個Task對象,然后Start,這種方式中規中矩,但是實踐中,通常采用更方便快捷的方式

Task.Run(() => Console.WriteLine("Foo"));

這種方式直接運行了Task,不像上面的方法還需要調用Start();

Task.Run方法是Task類中的靜態方法,接受的參數是委托。返回值是為該Task對象。

Task.Run(Action)

Task.Run<TResult>(Func<Task<TResult>>)

Task構造方法還有一個重載函數如下:

Task 構造函數 (Action, TaskCreationOptions),對應的Task泛型版本也有類似構造函數。TaskCreationOptions參數指示Task創建和執行的可選行為。常用的參數LongRunning。

默認情況下,Task任務是由線程池線程異步執行的。如果是運行時間很長的操作,使用LongRunning 參數暗示任務調度器,將這個任務放在非線程池上運行。通常不需要用這個參數,除非通過性能測試覺得使用該參數能有更好的性能,才使用。

任務等待

默認情況下,Task任務是由線程池線程異步執行。要知道Task任務的是否完成,可以通過task.IsCompleted屬性獲得,也可以使用task.Wait來等待Task完成。Wait會阻塞當前線程。 

  1. static void Main(string[] args) 
  2.         { 
  3.             Task Task1=Task.Run(() => { Thread.Sleep(5000); 
  4.             Console.WriteLine("Foo"); 
  5.                 Thread.Sleep(5000); 
  6.             }); 
  7.             Console.WriteLine(Task1.IsCompleted); 
  8.             Task1.Wait();//阻塞當前線程 
  9.             Console.WriteLine(Task1.IsCompleted); 
  10.         } 

Wait方法有個重構方法,簽名如下:public bool Wait(int millisecondsTimeout),接受一個時間。如果在設定時間內完成就返回true,否則返回false。如下的代碼:

  1. static void Main(string[] args) 
  2.         { 
  3.             Task Task1=Task.Run(() => { Thread.Sleep(5000); 
  4.             Console.WriteLine("Foo"); 
  5.                 Thread.Sleep(5000); 
  6.             }); 
  7.  
  8.             Console.WriteLine("Task1.IsCompleted:{0}",Task1.IsCompleted); 
  9.             bool b=Task1.Wait(2000); 
  10.             Console.WriteLine("Task1.IsCompleted:{0}", Task1.IsCompleted); 
  11.             Console.WriteLine(b); 
  12.             Thread.Sleep(9000); 
  13.             Console.WriteLine("Task1.IsCompleted:{0}", Task1.IsCompleted); 
  14.        } 

運行結果為:

Task類使用總結

獲得返回值 

要獲得返回值,就要用到Task的泛型版本了。 

  1. static void Main(string[] args) 
  2.         { 
  3.             Task<int> Task1 = Task.Run<int>(() => { Thread.Sleep(5000); return Enumerable.Range(1, 100).Sum(); }); 
  4.             Console.WriteLine("Task1.IsCompleted:{0}",Task1.IsCompleted); 
  5.             Console.WriteLine("Task1.IsCompleted:{0}", Task1.Result);//如果方法未完成,則會等待直到計算完成,得到返回值才運行下去。 
  6.             Console.WriteLine("Task1.IsCompleted:{0}", Task1.IsCompleted); 
  7.         } 

結果如下:

Task類使用總結

異常拋出

和線程不同,Task中拋出的異常可以捕獲,但是也不是直接捕獲,而是由調用Wait()方法或者訪問Result屬性的時候,由他們獲得異常,將這個異常包裝成AggregateException類型,再重新拋出捕獲。 

  1. static void Main(string[] args) 
  2.         { 
  3.             try 
  4.             { 
  5.                 Task<int> Task1 = Task.Run<int>(() => { throw new Exception("xxxxxx"); return 1; }); 
  6.                 Task1.Wait(); 
  7.             } 
  8.             catch (Exception ex)//error的類型為System.AggregateException 
  9.             { 
  10.                 Console.WriteLine(ex.StackTrace); 
  11.                 Console.WriteLine("-----------------"); 
  12.                 Console.WriteLine(ex.InnerException.StackTrace); 
  13.             } 
  14.         } 

如上代碼,運行結果如下:

Task類使用總結

可以看到異常真正發生的地方。

對于某些匿名的Task(通過 Task.Run方法生成的,不調用wait,也不關心是否運行完成),某些情況下,記錄它們的異常錯誤也是有必要的。這些異常稱作未觀察到的異常(unobserved exceptions)。可以通過訂閱一個全局的靜態事件TaskScheduler.UnobservedTaskException來處理這些異常。只要當一個Task有異常,并且在被垃圾回收的時候,才會觸發這一個事件。如果Task還處于被引用狀態,或者只要GC不回收這個Task,這個UnobservedTaskException事件就不會被觸發

例子: 

  1. static void Main(string[] args) 
  2.         { 
  3.             TaskScheduler.UnobservedTaskException += UnobservedTaskException; 
  4.             Task.Run<int>(() => { throw new Exception("xxxxxx"); return 1; }); 
  5.             Thread.Sleep(1000); 
  6.        } 
  7.  
  8.         static void UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
  9.         { 
  10.             Console.WriteLine(e.Exception.Message); 
  11.             Console.WriteLine(e.Exception.InnerException.Message); 
  12.         } 

這樣的代碼直到程序運行完成也為未能觸發UnobservedTaskException,因為GC沒有開始做垃圾回收。

在代碼中加入 GC.Collect(); 

  1. static void Main(string[] args) 
  2.         { 
  3.             TaskScheduler.UnobservedTaskException += UnobservedTaskException; 
  4.             Task.Run<int>(() => { throw new Exception("xxxxxx"); return 1; }); 
  5.             Thread.Sleep(1000); 
  6.             GC.Collect(); 
  7.             GC.WaitForPendingFinalizers(); 
  8.         } 

運行后得到如下:

Task類使用總結

延續任務

延續任務就是說當一個Task完成后,繼續運行下一個任務。通常有2種方法實現。

一種是使用GetAwaiter方法。GetAwaiter方法返回一個TaskAwaiter結構,該結構有一個OnCompleted事件,只需對OnCompleted事件賦值,即可在完成后調用該事件。 

  1. static void Main(string[] args) 
  2.         { 
  3.             Task<int> Task1 = Task.Run<int>(() => { return Enumerable.Range(1, 100).Sum(); }); 
  4.             var awaiter = Task1.GetAwaiter(); 
  5.             awaiter.OnCompleted(() => 
  6.             { 
  7.                 Console.WriteLine("Task1 finished"); 
  8.                 int result = awaiter.GetResult(); 
  9.                 Console.WriteLine(result); // Writes result 
  10.             }); 
  11.             Thread.Sleep(1000); 
  12.         } 

運行結果如下:

Task類使用總結

此處調用GetResult()的好處在于,一旦先前的Task有異常,就會拋出該異常。而且該異常和之前演示的異常不同,它不需要經過AggregateException再包裝了。

另一種延續任務的方法是調用ContinueWith方法。ContinueWith返回的任然是一個Task類型。ContinueWith方法有很多重載,算上泛型版本,差不多40個左右的。其中最常用的,就是接受一個Action或者Func委托,而且,這些委托的第一個傳入參數都是Task類型,即可以訪問先前的Task對象。示例: 

  1. static void Main(string[] args) 
  2.         { 
  3.             Task<int> Task1 = Task.Run<int>(() => {return Enumerable.Range(1, 100).Sum(); }); 
  4.             Task1.ContinueWith(antecedent => { 
  5. Console.WriteLine(antecedent.Result); 
  6. Console.WriteLine("Runing Continue Task"); 
  7. }); 
  8.             Thread.Sleep(1000); 
  9.         } 

使用這種ContinueWith方法和GetAwaiter都能實現相同的效果,有點小區別就是ContinueWith如果獲取Result的時候有異常,拋出的異常類型是經過AggregateException包裹的,而GetAwaiter()后的OnCompleted所調用的方法中,如果出錯,直接拋出異常。 

生成Task的另一種方法,TaskCompletionSource 

使用TaskCompletionSource很簡單,只需要實例化它即可。TaskCompletionSource有一個Task屬性,你可以對該屬性暴露的task做操作,比如讓它wait或者ContinueWith等操作。當然,這個task由TaskCompletionSource完全控制。TaskCompletionSource<TResult>類中有一些成員方法如下:

  1. public class TaskCompletionSource<TResult> 
  2. public void SetResult (TResult result); 
  3. public void SetException (Exception exception); 
  4. public void SetCanceled(); 
  5. public bool TrySetResult (TResult result); 
  6. public bool TrySetException (Exception exception); 
  7. public bool TrySetCanceled(); 
  8. ... 

調用以上方法意味著對Task做狀態的改變,將狀態設成completed,faulted或者 canceled。這些方法只能調用一次,不然會有異常。Try的方法可以調多次,只不過返回false而已。

通過一些技巧性的編碼,將線程和Task協調起來,通過Task獲得線程運行的結果。

示例代碼: 

  1. static void Main(string[] args) 
  2.         { 
  3.             var tcs = new TaskCompletionSource<int>(); 
  4.             new Thread(() => { 
  5.                 Thread.Sleep(5000); 
  6.                 int i = Enumerable.Range(1, 100).Sum(); 
  7.                 tcs.SetResult(i); }).Start();//線程把運行計算結果,設為tcs的Result。 
  8.             Task<int> task = tcs.Task; 
  9.             Console.WriteLine(task.Result); //此處會阻塞,直到匿名線程調用tcs.SetResult(i)完畢 
  10.         } 

說明一下以上代碼:

tcs是TaskCompletionSource<int>的一個實例,即這個Task返回的肯定是一個int類型。

獲得tcs的Task屬性,讀取并打印該屬性的值。那么 Console.WriteLine(task.Result);其實是會阻塞的,直到task的result被賦值之后,才會取消阻塞。而對task.result的賦值正在一個匿名線程中做的。也就是說,一直等到匿名線程運行結束,把運行結果賦值給tcs后,task.Result的值才會被獲得。這正是變相的實現了線程同步的功能,并且可以獲得線程的運行值。而此時的線程并不是運行在線程池上的

我們可以定義一個泛型方法,來實現一個Task對象,并且運行Task的線程不是線程池線程:

  1. Task<TResult> Run<TResult>(Func<TResult> function) 
  2.         { 
  3.             var tcs = new TaskCompletionSource<TResult>(); 
  4.             Thread t = new Thread(() => 
  5.              { 
  6.                  try { tcs.SetResult(function()); } 
  7.                  catch (Exception ex) { tcs.SetException(ex); } 
  8.              }); 
  9.             t.IsBackground = true
  10.             t.Start();//啟動線程 
  11.             return tcs.Task; 
  12.         } 

比如什么一個泛型方法,接受的參數是Func委托,返回的是Task類型。

該方法中啟動一個線程t,把t設為后臺線程,該線程運行的內容就是傳入的Func委托,并將Func委托的運行后的返回值通過tcs.SetResult賦給某個task。同時,如果有異常的話,就把異常賦給,某個task,然后將這個task返回。這樣,直到線程運行完畢,才能得到task.Result的值。調用的時候: 

  1. Task<int> task = Run(() => { Thread.Sleep(5000); return Enumerable.Range(1, 100).Sum(); }); 
  2. Console.Write(task.Result);//這句會阻塞當前線程,直到task的result值被賦值才行。 

TaskCompletionSource的另一個強大用處,是可以創建Task,而不綁定任何線程,比如,我們可以通過TaskCompletionSource實現對某一個方法的延遲調用。

代碼示例: 

  1. static Task<int> delayFunc() 
  2.         { 
  3.             var tcs = new TaskCompletionSource<int>(); 
  4.             var timer = new System.Timers.Timer(5000) { AutoReset = false }; 
  5.             timer.Elapsed += (sender, e) => { 
  6.                 timer.Dispose(); 
  7.                 int i = Enumerable.Range(1, 100).Sum(); 
  8.                 tcs.SetResult(i); 
  9.             }; 
  10.  
  11.             timer.Start(); 
  12.             return tcs.Task; 
  13.         } 

說明:

delayFunc()方法使用了一個定時器,5秒后,定時器事件觸發,將i的值賦給某個task的result。返回的是tcs.Task屬性,調用方式: 

  1. var task = delayFunc(); 
  2. Console.Write(task.Result); 

task變量得到賦值后,要讀取Result值,必須等到tcs.SetResult(i);運行完成才行。這就相當于實現了延遲某個方法。

當然Task自身提供了Delay方法,使用方法如下: 

  1. Task.Delay (5000).GetAwaiter().OnCompleted (() => Console.WriteLine (42)); 
  2. 或者: 
  3. Task.Delay (5000).ContinueWith (ant => Console.WriteLine (42)); 

Delay方法是相當于異步的Thread.Sleep();

---------------------------------

參考資料:《C# 5.0 IN A NUTSHELL》

MSDN官方資料

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

镇安县| 新河县| 高陵县| 北票市| 海林市| 怀来县| 彭山县| 舞阳县| 宜兰县| 盐池县| 贵南县| 宜昌市| 开远市| 鄂托克前旗| 祁门县| 遵义县| 宁晋县| 宿州市| 云浮市| 荔波县| 湄潭县| 大足县| 靖西县| 县级市| 区。| 清涧县| 博乐市| 富川| 玉树县| 三台县| 丹阳市| 本溪市| 临潭县| 四会市| 辽中县| 宣武区| 茶陵县| 杭锦旗| 黔西县| 鞍山市| 新余市|