C#並行和多線程編程 3——認識和使用Task

來源:互聯網
上載者:User

標籤:result   關注   void   src   結果   多線程編程   執行   rar   string   

 

  對於多線程,我們經常使用的是Thread。在我們瞭解Task之前,如果我們要使用多核的功能可能就會自己來開線程,然而這種執行緒模式在.net 4.0之後被一種稱為基於“任務的編程模型”所衝擊,因為task會比thread具有更小的效能開銷,不過大家肯定會有疑惑,任務和線程到底有什麼區別呢?

 任務和線程的區別:

1、任務是架構線上程之上的,也就是說任務最終還是要拋給線程去執行。

2、任務跟線程不是一對一的關係,比如開10個任務並不是說會開10個線程,這一點任務有點類似線程池,但是任務相比線程池有很小的開銷和精確的控制。

 

 一、認識Task和Task的基本使用

1、認識Task

首先來看一下Task的繼承結構。Task標識一個非同步作業。

可以看到Task和Thread一樣,位於System.Threading命名空間下,這也就是說他們直接有密不可分的聯絡。下面我們來仔細看一下吧!

 

2、建立Task

建立Task的方法有兩種,一種是直接建立——new一個出來,一種是通過工廠建立。下面來看一下這兩種建立方法:

        //第一種建立方式,直接執行個體化         var task1 = new Task(() =>         {            //TODO you code         });

這是最簡單的建立方法,可以看到其建構函式是一個Action,其建構函式有如下幾種,比較常用的是前兩種。

        //第二種建立方式,工廠建立         var task2 = Task.Factory.StartNew(() =>         {            //TODO you code         });

這種方式通過靜態工廠,建立以個Task並運行。下面我們來建一個控制台項目,示範一下,代碼如下:

要添加System.Threading.Tasks命名控制項引用。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace TaskDemo{   class Program   {      static void Main(string[] args)      {         var task1 = new Task(() =>         {            Console.WriteLine("Hello,task");         });         task1.Start();         var task2 = Task.Factory.StartNew(() =>         {            Console.WriteLine("Hello,task started by task factory");         });         Console.Read();      }   }}

 這裡我分別用兩種方式建立兩個task,並讓他們運行。可以看到通過建構函式建立的task,必須手動Start,而通過工廠建立的Task直接就啟動了。

下面我們來看一下Task的聲明周期,編寫如下代碼:

      var task1 = new Task(() =>         {            Console.WriteLine("Begin");            System.Threading.Thread.Sleep(2000);            Console.WriteLine("Finish");         });         Console.WriteLine("Before start:" + task1.Status);         task1.Start();         Console.WriteLine("After start:" + task1.Status);         task1.Wait();         Console.WriteLine("After Finish:" + task1.Status);         Console.Read();

  task1.Status就是輸出task的目前狀態,其輸出結果如下:

可以看到調用Start前的狀態是Created,然後等待分配線程去執行,到最後執行完成。

從我們可以得出Task的簡略生命週期:

Created:表示預設初始化任務,但是“工廠建立的”執行個體直接跳過。

WaitingToRun: 這種狀態表示等待任務調度器分配線程給任務執行。

RanToCompletion:任務執行完畢。

 

 二、Task的任務控制

  Task最迷人的地方就是他的任務控制了,你可以很好的控制task的執行順序,讓多個task有序的工作。下面來詳細說一下:

1、Task.Wait

在上個例子中,我們已經使用過了,task1.Wait();就是等待任務執行完成,我們可以看到最後task1的狀態變為Completed。

 

2、Task.WaitAll

看字面意思就知道,就是等待所有的任務都執行完成,下面我們來寫一段代碼示範一下:

    static void Main(string[] args)      {         var task1 = new Task(() =>         {            Console.WriteLine("Task 1 Begin");            System.Threading.Thread.Sleep(2000);            Console.WriteLine("Task 1 Finish");         });         var task2 = new Task(() =>         {            Console.WriteLine("Task 2 Begin");            System.Threading.Thread.Sleep(3000);            Console.WriteLine("Task 2 Finish");         });                  task1.Start();         task2.Start();         Task.WaitAll(task1, task2);         Console.WriteLine("All task finished!");         Console.Read();      }

其輸出結果如下:

可以看到,任務一和任務二都完成以後,才輸出All task finished!

 

3、Task.WaitAny

這個用發同Task.WaitAll,就是等待任何一個任務完成就繼續向下執行,將上面的代碼WaitAll替換為WaitAny,輸出結果如下:

 

4、Task.ContinueWith

就是在第一個Task完成後自動啟動下一個Task,實現Task的延續,下面我們來看下他的用法,編寫如下代碼:

static void Main(string[] args)      {         var task1 = new Task(() =>         {            Console.WriteLine("Task 1 Begin");            System.Threading.Thread.Sleep(2000);            Console.WriteLine("Task 1 Finish");         });         var task2 = new Task(() =>         {            Console.WriteLine("Task 2 Begin");            System.Threading.Thread.Sleep(3000);            Console.WriteLine("Task 2 Finish");         });                  task1.Start();         task2.Start();         var result = task1.ContinueWith<string>(task =>         {            Console.WriteLine("task1 finished!");            return "This is task result!";         });         Console.WriteLine(result.Result.ToString());         Console.Read();      }

執行結果如下:

可以看到,task1完成之後,開始執行後面的內容,並且這裡我們取得task的傳回值。

在每次調用ContinueWith方法時,每次會把上次Task的引用傳入進來,以便檢測上次Task的狀態,比如我們可以使用上次Task的Result屬性來擷取傳回值。我們還可以這麼寫:

var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })                            .ContinueWith<bool>(s => { return true; })                            .ContinueWith<string>(r =>                               {                                 if (r.Result)                                 {                                    return "Finished";                                 }                                 else                                 {                                    return "Error";                                 }                              });         Console.WriteLine(SendFeedBackTask.Result);

首先輸出Get some data,然後執行第二個獲得傳回值true,最後根據判斷返回Finished或error。輸出結果:

Get some Data!

Finished

其實上面的寫法簡化一下,可以這樣寫:

Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});

輸出One,這個可以看明白了吧~

 更多ContinueWith用法參見:http://technet.microsoft.com/zh-CN/library/dd321405

 

5、Task的取消

前面說了那麼多Task的用法,下面來說下Task的取消,比如我們啟動了一個task,出現異常或者使用者點擊取消等等,我們可以取消這個任務。

如何取消一個Task呢,我們通過cancellation的tokens來取消一個Task。在很多Task的Body裡麵包含迴圈,我們可以在輪詢的時候判斷IsCancellationRequested屬性是否為True,如果是True的話就return或者拋出異常,拋出異常後面再說,因為還沒有說異常處理的東西。

下面在代碼中看下如何?任務的取消,代碼如下:

var tokenSource = new CancellationTokenSource();         var token = tokenSource.Token;         var task = Task.Factory.StartNew(() =>         {            for (var i = 0; i < 1000; i++)            {               System.Threading.Thread.Sleep(1000);               if (token.IsCancellationRequested)               {                  Console.WriteLine("Abort mission success!");                  return;               }            }         }, token);         token.Register(() =>         {            Console.WriteLine("Canceled");         });         Console.WriteLine("Press enter to cancel task...");         Console.ReadKey();         tokenSource.Cancel();
         Console.ReadKey();//這句忘了加,程式退出了,看不到“Abort mission success!“這個提示

這裡開啟了一個Task,並給token註冊了一個方法,輸出一條資訊,然後執行ReadKey開始等待使用者輸入,使用者點擊斷行符號後,執行tokenSource.Cancel方法,取消任務。其輸出結果如下:

 

好了,今天先說道這裡,明天繼續講task,接下來該說說task的異常處理和其他的一些用法,如果喜歡可以關注我,一有更新會馬上通知你。

C#並行和多線程編程 3——認識和使用Task

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.