淺談Task的用法
Task是用來實現多執行緒的類,在以前當版本中已經有了Thread及ThreadPool,為什麼還要提出Task類呢,這是因為直接操作Thread及ThreadPool,向執行緒中傳遞引數,獲取執行緒的返回值及執行緒當啟停都非常的麻煩,所以微軟的工程師對Thread進行了再封裝,這就是Task,可以這麼說Task是架構在Thread之上的,
所以多執行緒時Task是我們的首選。
Task類和Task<TResult>類,後者是前者的泛型版本。TResult型別為Task所呼叫方法的返回值。
主要區別在於Task建構函式接受的引數是Action委託,而Task<TResult>接受的是Func<TResult>委託
1 Task(Action) 2 Task<TResult>(Func<TResult>)
一、Task的宣告
Task的宣告有兩種方式:
a,通過new 的方式來宣告
1 Task objTask = new Task();
b.通過Task.Factory.StartNew的方式來宣告
1 Task.Factory.StartNew(MyMethod);
這兩種宣告方式的區別,第一種宣告方式開啟執行緒必須使用objTask.Start(),而通過Task.Factory.StartNew的方式則不用。
二、Task常用方法
a.啟動一個任務
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。
b.任務等待
預設情況下,Task任務由執行緒池非同步執行,想要知道Task任務是否完成,可以通過Task.IsComplated屬性獲得
也可以使用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 }
輸出的結果為:
C.獲取返回值
要獲得返回值,就要用到Task的泛型版本了。 說到Task的返回值就不得不說await和async關鍵字了。
當函式使用async標記後,返回值必須為void,Task,Task<T>,當返回值為Task<T>時,函式內部只需要返回T型別,編譯器會自動包裝成Task<T>型別
await關鍵字必須在具有async標記的函式內使用。舉個例子:
1 static void Main(string[] args) 2 { 3 Console.WriteLine("呼叫主執行緒"); 4 Task<string> s = Testasync(); 5 Console.WriteLine(s.Result); 6 Console.ReadKey(); 7 8 } 9 static async Task<string> Testasync() 10 { 11 Console.WriteLine("執行Task之前" + Thread.CurrentThread.ManagedThreadId); 12 13 Task<string> t= Task.Run<string>(() => 14 { 15 Console.WriteLine("執行Task" + Thread.CurrentThread.ManagedThreadId); 16 Thread.Sleep(9000); 17 return "我是測試執行緒"; 18 }); 19 Console.WriteLine("執行Task之後" + Thread.CurrentThread.ManagedThreadId); 20 var result = await t; 21 return result; 22 }
從執行結果可以看出:
1)在async標識的方法體裡面,如果沒有await關鍵字的出現,那麼這種方法和呼叫普通的方法沒什麼區別(就是說async和await是成對出現的,沒有await的async是沒有意義的)
(2)在async標識的方法體裡面,在await關鍵字出現之前,還是主執行緒順序呼叫的,直到await關鍵字的出現才會出現執行緒阻塞。
(3)await關鍵字可以理解為等待方法執行完畢,除了可以標記有async關鍵字的方法外,還能標記Task物件,表示等待該執行緒執行完畢。所以await關鍵字並不是針對於async的方法,而是針對async方法所返回給我們的Task。
D.延續任務
所謂延續任務,就是說在任務執行完成之後繼續執行任務,有兩種方法
第一種,使用一種是使用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 }
第二種,使用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 }
E,延遲任務
Task.Delay()方法是相當於非同步的Thread.Sleep();
&n