淺談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();

 


免責聲明!

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



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