程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET 4並行(多核)編程系列之二 從Task開始

.NET 4並行(多核)編程系列之二 從Task開始

編輯:關於.NET

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

本篇文章的議題如下:

1.Task基礎介紹

2.Task的創建

3.獲取Task的執行結果

4. 補充細節

1.Task基礎介紹

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

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

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

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

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

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

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

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

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

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

Task.Factory.StartNew(() =>
             {
                 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交給底下的線程去執行。

下面就看看代碼:

代碼

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(() => printMessage());
             // use a lambda expression and an anonymous method
             Task task4 = new Task(() =>
             {
                 printMessage();
             });
             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<object>的委托,其中的那個object就是我們傳入的參數。還是給大家舉個例子:

代碼

using System;
using System.Threading.Tasks;
namespace Listing_04
{
     class Listing_04
     {
         static void Main(string[] args)
         {
             string[] messages = { "First task", "Second task",
"Third task", "Fourth task" };
             foreach (string msg in messages)
             {
                 Task myTask = new Task(obj => printMessage((string)obj), msg);
                 myTask.Start();
             }
             // wait for input before exiting
             Console.WriteLine("Main method complete. Press enter to finish.");
             Console.ReadLine();
         }
         static void printMessage(string message)
         {
             Console.WriteLine("Message: {0}", message);
         }
     }
}

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

想向Task傳入參素,只能用System.Action<object>

3.獲取Task的執行結果

如果要獲取Task的結果,那麼在創建Task的時候,就要采用Task<T>來實例化一個Task,其中的那個T就是task執行完成之後返回結果的類型。之後采用Task實例的Result屬性就可以獲取結果。

代碼顯示如下:

代碼

static void Main(string[] args)
         {
             // create the task
             Task<int> task1 = new Task<int>(() =>
             {
                 int sum = 0;
                 for (int i = 0; i < 100; i++)
                 {
                     sum += i;
                 }
                 return sum;
             });

             task1.Start();
             // write out the result
             Console.WriteLine("Result 1: {0}", task1.Result);

             Console.ReadLine();
         }

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

下面的代碼展示了如何通過Task.Factory.StartNew<T>()創建一個Task,並且獲取結果:

代碼

static void Main(string[] args)
         {
             // create the task
             Task<int> task1 = Task.Factory.StartNew<int>(() =>
             {
                 int sum = 0;
                 for (int i = 0; i < 100; i++)
                 {
                     sum += i;
                 }
                 return sum;
             });
             // write out the result
             Console.WriteLine("Result 1: {0}", task1.Result);

             Console.ReadLine();
         }

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved