天天看點

.NET 4 并行(多核)程式設計系列之二 從Task開始

.NET 4 并行(多核)程式設計系列之二 從Task開始

    前言:我們一步步的從簡單的開始講述,還是沿用我一直的方式:慢慢演化,步步為營。

    本篇文章的議題如下:

    1.Task基礎介紹

    2.Task的建立

    3.擷取Task的執行結果

  4. 補充細節

  系列文章連結:

<a href="http://www.cnblogs.com/yanyangtian/archive/2010/06/24/1764098.html">  .NET 并行(多核)程式設計系列之七 共享資料問題和解決概述</a>

    首先我們還是來看看一段簡單的代碼:

    這裡展示的隻是一段簡單的代碼,不能顯示出并行程式設計的特點。但是我們還是從最基本的開始看,慢慢進入深一點的話題。

    如果你曾經用過.NET 中的多線程程式設計,比較一下,就會發現:這段代碼雖然在底層還是使用了多線程,但是寫法上卻簡化了很多,一行代碼就實作了一個并行程式設計。

    下面我們就從Task類開始談。

    Task類是Task Programming Library(TPL)中最核心的一個類,下面我将會像大家展示如何使用一些方法來建立不同類型的Task,

取消Task,等待Task執行完成,擷取Task執行後的結果和對異常進行處理。

    在開始讨論之前,我們首先快速的看看之前的代碼:

    這個命名空間将會是我們之後在講述并行程式設計經常使用的一個。這個空間包含了很多與并行程式設計有關的類。

    還有一個要你使用的命名空間是:System.Threading,大家對這個應該比較熟悉了,之前的多線程程式設計常常使用到,這個空間下包含了一些在并行程式設計中用來協調資料的一些類。

    上面代碼中,最主要的代碼如下:

  Task.Factory.StartNew(() =&gt;

            {

                Console.WriteLine("Hello World");

            });

    我們用靜态方法:Task.Factory.StartNew()來建立了一個最簡單的Task--在螢幕上列印一句話。這段代碼确實簡單,而且都沒有任何輸入和需要傳回的結果。

    下面我們就正式進入議題:

    2.Task的建立   

  

    如果隻是建立一個簡單的Task,我們隻要為該Task提供一個執行體就行了,執行體可以是一個委托delegate或者action。我們之前展示的那段代碼就是采用了lambda表達式來作為Task的執行體。

    2.1 建立一個簡單的Task

    為了執行一個簡單的Task,一般進行以下步驟:

    首先,要建立一個Task類的執行個體,

    然後,傳入一個System.Action委托,這個委托中的方法就是這個Task運作時你要執行的方法,而且這個委托必須作為Task構造函數的一個參數傳入。我們在傳入委托作為參數的時候有多種方式:傳入匿名委托,

Lambda表達式或者一個顯示什麼方法的委托。

    最後,調用Task執行個體的Start()方法來運作。

    當這個Task執行個體開始運作的時候,它就被傳給了内部的一個task scheduler,這個scheduler負責把我們建立的task交給底下的線程去執行。

    下面就看看代碼:

.NET 4 并行(多核)程式設計系列之二 從Task開始
.NET 4 并行(多核)程式設計系列之二 從Task開始

代碼

using System;

using System.Threading.Tasks;

namespace Listing_02

{

    class Listing_02

    {

        static void Main(string[] args)

        {

            // use an Action delegate and a named method

            Task task1 = new Task(new Action(printMessage));

            // use a anonymous delegate

            Task task2 = new Task(delegate

                printMessage();

            // use a lambda expression and a named method

            Task task3 = new Task(() =&gt; printMessage());

            // use a lambda expression and an anonymous method

            Task task4 = new Task(() =&gt;

            task1.Start();

            task2.Start();

            task3.Start();

            task4.Start();

            // wait for input before exiting

            Console.WriteLine("Main method complete. Press enter to finish.");

            Console.ReadLine();

        }

        static void printMessage()

            Console.WriteLine("Hello World");

    }

}

    不知道大家注意到了沒有,上面代碼建立Task的方法和我們之前的第一段代碼的建立Task的方法不同。在之前我們采用的是Task.Factory.StartNew()方法來建立的,這個方法建立Task并且開始運作Task,其實兩端代碼的結果是一樣的,這裡給出一點建議:如果這是想簡單的建立一個Task,那麼使用Factory.NewStart()來建立,很簡便,如果像對所建立的Task附加更多的定制和設定特定的屬性,那麼還是得一步一步的按照我們說的那些步驟來。(詳細的我們後續會介紹的)

    2.1 為建立的Task傳入參數  

    我們之前提過,在建立Task的時候,我們在構造函數中傳入了一個System.Action的委托,如果我們想要把一些參數傳入到Task中,那麼我 們可以傳入System.Action&lt;object&gt;的委托,其中的那個object就是我們傳入的參數。還是給大家舉個例子:

.NET 4 并行(多核)程式設計系列之二 從Task開始
.NET 4 并行(多核)程式設計系列之二 從Task開始

namespace Listing_04

    class Listing_04

            string[] messages = { "First task", "Second task",

"Third task", "Fourth task" };

            foreach (string msg in messages)

                Task myTask = new Task(obj =&gt; printMessage((string)obj), msg);

                myTask.Start();

            }

        static void printMessage(string message)

            Console.WriteLine("Message: {0}", message);

    注意:我們在傳入參數後,必須把參數轉換為它們原來的類型,然後再去調用相應的方法。例子中,因為System.Action對應的方法是printMessage()方法,而這個方法的要求的參數類型是string,是以要轉換為string。

  想向Task傳入參素,隻能用System.Action&lt;object&gt;

     

   如果要擷取Task的結果,那麼在建立Task的時候,就要采用Task&lt;T&gt;來執行個體化一個Task,其中的那個T就是task執行完成之後傳回結果的類型。之後采用Task執行個體的Result屬性就可以擷取結果。

    代碼顯示如下:

.NET 4 并行(多核)程式設計系列之二 從Task開始
.NET 4 并行(多核)程式設計系列之二 從Task開始

 static void Main(string[] args)

            // create the task

            Task&lt;int&gt; task1 = new Task&lt;int&gt;(() =&gt;

                int sum = 0;

                for (int i = 0; i &lt; 100; i++)

                {

                    sum += i;

                }

                return sum;

            // write out the result

            Console.WriteLine("Result 1: {0}", task1.Result);

    隻有在task執行完成之後,才能擷取到Result的值。

    下面的代碼展示了如何通過Task.Factory.StartNew&lt;T&gt;()建立一個Task,并且擷取結果:

.NET 4 并行(多核)程式設計系列之二 從Task開始
.NET 4 并行(多核)程式設計系列之二 從Task開始

  static void Main(string[] args)

            Task&lt;int&gt; task1 = Task.Factory.StartNew&lt;int&gt;(() =&gt;

  4. 補充細節   

  在建立Task的時候,Task有很多的構造函數的重載,一個主要的重載就是傳入TaskCreateOptions的枚舉:     

    TaskCreateOptions.None:用預設的方式建立一個Task

    TaskCreateOptions.PreferFairness:請求scheduler盡量公平的執行Task(後續文章會将是,Task和線程一樣,有優先級的)

    TaskCreateOptions.LongRunning:聲明Task将會長時間的運作。

    TaskCreateOptions.AttachToParent:因為Task是可以嵌套的,是以這個枚舉就是把一個子task附加到一個父task中。

    最後要提到的一點就是,我們可以在Task的執行體中用Task.CurrentId來傳回Task的唯一表示ID(int)。如果在Task執行體外使用這個屬性就會得到null。

    (熟悉WF的朋友,可以比較Task和WF的一些差別,因為我認為它們的設計思想很相近)

    今天就到這裡了,多謝大家! 

  http://www.cnblogs.com/yanyangtian

  

繼續閱讀