[深入學習C#]C#實現多線程的方式:Task——任務

來源:互聯網
上載者:User

標籤:number   opera   line   read   repr   sql   通知   調度   依賴   

簡介

  .NET 4包含新名稱空間System.Threading.Tasks,它 包含的類抽象出了線程功能。 在後台使用ThreadPool。 任務表示應完成的某個單元的工作。 這個單元的工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主調線程。 使用任務不僅可以獲得一個抽象層,還可以對底層線程進行很多控制。 
  在安排需要完成的工作時,任務提供了非常大的靈活性。 例如,可 以定義連續的工 作—— 在一個任務完成後該執行什麼工作。 這可以區分任務成功與否。 另外,還可以在階層中安排任務。例如,父任務可以建立新的子任務。 這可以建立一種依賴關係,這樣,取消父任務,也會取消其子任務。

啟動任務

  要啟動任務,可 以使用 TaskFactory類 或 Task類 的建構函式和 Start()方法。Task類的建構函式在建立任務上提供的靈活性較大。 
  在啟動任務時,會建立Task類 的一個執行個體,利用Action或Action<object>委託不帶參數或帶一個object參數 ,可以指定應啟動並執行代碼,這類似於Thread類 。下面定義了一個無參數的方法。 在實現代碼中,把任務的ID寫入控制台中: 
  

static void TaskMethod(){    Console.WriteLine("running in a task");    Console.WriteLine("Task id: {0}",Task.CurrentId);}

  在上面的代碼中,可 以看到啟動新任務的不同方式。第一種方式 使用執行個體化TaskFactory類 ,在其中把 TaskMedlod()方 法傳遞給StartNew()方法,就會立即啟動任務。 第二種方式使用 Task類的建構函式。 執行個體化 Task對象時,任務不會立即運行,而是指定 Created狀態。接著調用 Task類的Start()方法,來啟動任務。 使用Task類 時,除了調用 Start()方法,還可以調用RunSynchronously()方法。這樣,任務也會啟動,但在調用者的當前線程中它正在運行,調用者需要一直等待到該任務結束。 預設情況下,任務是非同步啟動並執行。 
  

    //using task factory    TaskFactory tf = new TaskFactory();    Task t1 = tf.StartNew(TaskMethod);    //using the task factory via a task    Task t2 = Task.TaskFactory.StartNew(TaskMethod);    //using Task constructor    Task t3 = new Task(TaskMethod);    t3.Start();

  使用Task類的建構函式和TaskFactory類的StartNew()方法時,都可以傳遞TaskCreationOptions枚舉中的值。設定LongRunning選項,可以通知任務調度器,該任務需要較長時間執行,這樣調度器更可能使用新線程。如果該任務應關聯到父任務上,而父任務取消了,則該任務也應取消,此時應設定 AuachToParent選項。PreferFairness的值表示,調度器應提取出已在等待的第一個任務。 如果一個任務在另一個任務內部建立,這就不是預設情況 。如果任務使用子任務建立了其他工作,子任務就優先於其他任務。 它們不會排線上程池隊列中的最後。 如果這些任務應以公平的方式與所有其他任務一起處理,就設定該選項為PreferFairness。 
  

Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness);t4.Start();
連續任務

   通過任務,可 以指定在任務完成後,應開始運行另一個特定任務,例如,一個使用前一個任務的結果的新任務,如 果前一個任務失敗了,這個任務就應執行一些清理工作。 
  任務處理常式或者不帶參數或者帶一個對象參數,而連續處理常式有一個 Task類 型的參數,這裡可以訪問起始任務的相關資訊: 
  

static void DoOnFirst(){    Console.WriteLine("doing some task {0}",Task.CurrentId);    Thread.Sleep(3000);}static void DoOnSecond(Task t){    Console.WriteLine("task {0} finished", t.Id);    Console.WriteLine("this task id {0}", Task.CurrentId);    Console.WriteLine("do some cleanup");    Thread.Sleep(3000);}

   連續任務通過在任務上調用ContinueWith()方法來定義。也可以使用TaskFactory類來定義。t1.ContinueWith(DoOnSecond)方 法表示,調用DoOnSecond()方法的新任務應在任務t1結束時立即啟動。在一個任務結束時,可以啟動多個任務,連續任務也可以有另一個連續任務,如下面的例子所示:

Task t1 = new Task(DoOnFirst);Task t2 = t1.ContinueWith(DoOnSecond);Task t3 = t1.ContinueWith(DoOnSecond);Task t4 = t2.ContinueWith(DoOnSecond);

   
   無論前一個任務是如何結束的,前 面的連續任務總是在前一個任務結束時啟動。 使用TaskContinuationOptions枚舉中的值,可以指定,連續任務只有在起始任務(或失敗)結束時啟動。一些可能的值是OnlyOnFaulted、 NotOnFaulted、 OnlyOnCanceled、 NotOnCanceled和OnlyOnRanToCompletion。 
  

Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);
任務階層

  利用任務連續性,可 以在一個任務結束後啟動另一個任務。 任務也可以構成一個階層。 一個任務啟動一個新任務時,就啟動了一個父/子階層。 
  下面的程式碼片段在父任務內部建立一個任務。 建立子任務的代碼與建立父任務的代碼相同,唯一的區別是這個任務從另一個任務內部建立。 
  

static void ParentAndChild(){    var parent = new Task(ParentTask);    parent.Start();    Thread.Sleep(2000);    Console.WriteLine(parent.Status);    Thread.Sleep(4000);    Console.WriteLine(parent.Status);}static void ParentTask(){    Console.WriteLine("task id {0}", Task.CurrentId);    var child = new Task(ChildTask);    child.Start();    Thread.Sleep(1000);    Console.WriteLine("parent started child");}static void ChildTask(){    Console.WriteLine("child");    Thread.Sleep(5000);    Console.WriteLine("child finished");}

  如果父任務在子任務之前結束,父任務的狀態就顯示為WaitingForChildrenToComplete。 只要子任務也結束時,父任務的狀態就變成RanToCompletion。 當然,如 果父任務用TaskCreationOptions枚舉中的 DetachedFromParent建立子任務時,這就無效。

任務的結果

  任務結束時,它可以把一些有用的狀態資訊寫到共用對象中。這個共用對象必須是安全執行緒的。另一個選項是使用返回某個結果的任務。使用 Task類 的泛型版本,就可以定義返回某個結果的任務的傳回型別。 
  為了返回某個結果任務調用的方法可以聲明為帶任意傳回型別。樣本方法TaskWithResult()利用一個元組返回兩個int值。 該方法的輸入可以是void或object類型,如下所示: 
  

static Tuple<int, int> TaskWithResult(object division){    Tuple<int, int> div =(Tuple<int, int>)division;    int result = div.Item1/div.Item2;    int reminder = div.Item1%div.Item2;    Console.WriteLine("task creates a result...");    return Tuple.Create<int, int>(result, reminder);}

  定義一個調用 StartWithResult()方法的任務時,要使用泛型類Task<Tresult>。 泛型參數定義了傳回型別。通過建構函式,把這個方法傳遞給Func委 托,第二個參數定義了輸入值。 因為這個任務在object參數中需要兩個輸入值,所以還建立了一個元組。 接著啟動該任務。 Task執行個體t1的Result屬性被禁用,並一直等到該任務完成。任務完成後,Result屬性包含任務的結果。 
  

var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(8, 3));t1.Start();Console.WriteLine(t1.Result);t1.Wait();Console.WriteLine("result from task: {0} {1}",t1.Result.Item1, t1.Result.Item2);

  備忘:上例中,Task<Tresult>建構函式調用了TaskFactory.StartNew 方法的 (Func<Object, TResult>, Object)重載。 
  function 
    類型:System.Func<Object, TResult> 
    一個函數委託,可返回能夠通過任務獲得的將來結果。

  state 
    類型:System.Object 
    包含 function 委託所用資料的對象。

  因此我們可以知道,為什麼在執行個體化t1的時候,為什麼要建立一個新的Tuple對象了。

[深入學習C#]C#實現多線程的方式: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.