轉C# 線程知識--使用Task執行非同步作業

來源:互聯網
上載者:User

標籤:parent   def   from   第一個   知識   void   array   post   each   

在C#4.0之前需要執行一個複雜的非同步作業時,只能使用CLR線程池技術來執行一個任務。線程池執行非同步任務時,不知道任務何時完成,以及任務的在任務完成後不能擷取到傳回值。但是在C#4.0中引人了一個的任務(System.Threading.Tasks命名空間的類型)機制來解決非同步作業完成時間和完成後傳回值的問題。

1.使用Task類建立並執行簡單任務

    通過使用Task的建構函式來建立任務,並調用Start方法來啟動任務並執行非同步作業。建立任務時,必須傳遞一個Action或Action<Object>類型的委託回調方法,可以選擇的傳遞任務執行時說需要的資料對象等。Task類的建構函式如下:

        public Task(Action action);        public Task(Action<object> action, object state);        public Task(Action action, CancellationToken cancellationToken);        public Task(Action action, TaskCreationOptions creationOptions);        public Task(Action<object> action, object state, CancellationToken cancellationToken);        public Task(Action<object> action, object state, TaskCreationOptions creationOptions);        public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);        public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);

範例程式碼:

   1:  static void Main(string[] args)
   2:          {
   3:              Console.WriteLine("主線程執行業務處理.");
   4:              //建立任務
   5:              Task task = new Task(() => {
   6:                  Console.WriteLine("使用System.Threading.Tasks.Task執行非同步作業.");
   7:                  for (int i = 0; i < 10; i++)
   8:                  {
   9:                      Console.WriteLine(i);
  10:                  }
  11:              });
  12:              //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
  13:              task.Start();
  14:              Console.WriteLine("主線程執行其他處理");
  15:              //主線程掛起1000毫秒,等待任務的完成。
  16:              Thread.Sleep(1000);
  17:          }

任務調度結果:

2.等待任務的完成並擷取傳回值

     使用任務執行非同步作業時,最主要的是要後的任務完成時的傳回值。在任務類中有一個執行個體方法Wait(有許多重載版本)他能等待任務的完成,我們也可以通過Task類的衍生類別Task<TResult>建立一個非同步任務,並指定任務完成時傳回值的類型,這樣可以通過Task<TResult>的執行個體對象擷取到任務完成後的傳回值。建立一個非同步任務並執行0到100求和操作返回最後的計算結果,範例程式碼:

   1:  static void TaskWait() {
   2:              //建立任務
   3:              Task<int> task = new Task<int>(() =>
   4:              {
   5:                  int sum = 0;
   6:                  Console.WriteLine("使用Task執行非同步作業.");
   7:                  for (int i = 0; i < 100; i++)
   8:                  {
   9:                      sum+=i;
  10:                  }
  11:                  return sum;
  12:              });
  13:              //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
  14:              task.Start();
  15:   
  16:              Console.WriteLine("主線程執行其他處理");
  17:              //等待任務的完成執行過程。
  18:              task.Wait();
  19:              //獲得任務的執行結果
  20:              Console.WriteLine("任務執行結果:{0}", task.Result.ToString());
  21:  }

執行結果:

Task類還有一些靜態方法,WaitAll用於等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程和Wait用於等待提供的任一個 System.Threading.Tasks.Task 對象完成執行過程,這兩個方法都有一些重載版本。

//等待所有任務完成  public static void WaitAll(params Task[] tasks);//等待任意一個任務完成public static int WaitAny(params Task[] tasks);

3.使用ContinueWith方法在任務完成時啟動一個新任務

     在使用能夠Task類的Wait方法等待一個任務時或衍生類別的Result屬性獲得任務執行結果都有可能阻塞線程,為瞭解決這個問題可以使用ContinueWith方法,他能在一個任務完成時自動啟動一個新的任務來處理執行結果。

範例程式碼:

   1:  static void TaskContinueWith()
   2:          {
   3:              //建立一個任務
   4:              Task<int> task = new Task<int>(() =>
   5:              {
   6:                  int sum = 0;
   7:                  Console.WriteLine("使用Task執行非同步作業.");
   8:                  for (int i = 0; i < 100; i++)
   9:                  {
  10:                      sum += i;
  11:                  }
  12:                  return sum;
  13:              });
  14:              //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
  15:              task.Start();
  16:              Console.WriteLine("主線程執行其他處理");
  17:              //任務完成時執行處理。
  18:              Task cwt = task.ContinueWith(t => { 
  19:                  Console.WriteLine("任務完成後的執行結果:{0}", t.Result.ToString()); 
  20:              });
  21:              Thread.Sleep(1000);
  22:          }

執行結果:

上述樣本中任務不是等待完成來顯示執行結果,而是使用ContinueWith方法,它能夠知道任務在什麼時候完成並啟動一個新的任務來執行任務完成後的處理。ContinueWith方法具有一些重載版本,這些重載版本允許指定延續任務需要使用的資料、延續任務的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚舉值按位OR啟動並執行結果)等。

4.建立父子任務和任務工廠的使用

    通過Task類建立的任務是頂級任務,可以通過使用 TaskCreationOptions.AttachedToParent 標識把這些任務與建立他的任務相關聯,所有子任務全部完成以後父任務才會結束操作。樣本如下:

   1:  static void ParentChildTask() {
   2:              Task<string[]> parent = new Task<string[]>(state => {
   3:                  Console.WriteLine(state);
   4:                  string[] result=new string[2];
   5:                  //建立並啟動子任務
   6:                  new Task(() => { result[0]= "我是子任務1。"; },TaskCreationOptions.AttachedToParent).Start();
   7:                  new Task(() => { result[1] = "我是子任務2。"; }, TaskCreationOptions.AttachedToParent).Start();
   8:                  return result;
   9:              },"我是父任務,並在我的處理過程中建立多個子任務,所有子任務完成以後我才會結束執行。");
  10:              //任務處理完成後執行的操作
  11:              parent.ContinueWith(t => {
  12:                  Array.ForEach(t.Result, r=>Console.WriteLine(r));
  13:              });
  14:              //啟動父任務
  15:              parent.Start();
  16:              Console.Read();
  17:          }

執行結果:

    如果需要建立一組具有相同狀態的任務時,可以使用TaskFactory類或TaskFactory<TResult>類。這兩個類建立一組任務時可以指定任務的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler預設值。範例程式碼:

   1:          static void TaskFactoryApply()
   2:          {
   3:              Task parent = new Task(() =>
   4:              {
   5:                  CancellationTokenSource cts = new CancellationTokenSource(5000);
   6:                  //建立任務工廠
   7:                  TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
   8:                  //添加一組具有相同狀態的子任務
   9:                  Task[] task = new Task[]{
  10:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠裡的第一個任務。"); }),
  11:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠裡的第二個任務。"); }),
  12:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠裡的第三個任務。"); })
  13:                  };
  14:              });
  15:              parent.Start();
  16:              Console.Read();
  17:          }

執行結果:

5.任務內部實現和任務調度

    任務內部有一組構成任務狀態的屬性,標識任務的唯一Id、表示任務的執行狀態(TaskStatus)、任務建立時提供的回呼函數的引用和傳遞給回呼函數的資料對象AsyncState、對任務建立時的任務調度對象(TaskScheduler)的引用、對父任務的引用以及對執行內容的引用和ManualResetEventSlim對象的引用。Task類和Task<TResult>類都實現了標準的釋放資源的介面,允許在任務完成處理的時候使用Dispose方法釋放資源(關閉ManualResetEventSlim對象執行個體)。可以使用Task類的CurrentId屬性獲得正在執行的任務的Id,如果沒有任務在執行CurrentId傳回值為null,CurrentId是一個int?可空類型的屬性。任務執行的生命週期通過TaskStatus類型的一個值來表示,TaskStatus所包含的值:

public enum TaskStatus        {            Created = 0,            WaitingForActivation = 1,            WaitingToRun = 2,            Running = 3,            WaitingForChildrenToComplete = 4,            RanToCompletion = 5,            Canceled = 6,            Faulted = 7,        }

      我們可以通過Task類的Exception屬性獲得任務在執行過程中的所有異常

,Exception是一個AggregateException類型的屬性。Task類提供了IsCanceled、IsCompleted、IsFaulted屬性來獲得任務的完成狀態。通過ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync建立的後續任務都處於WaitingForActivation 狀態,這個狀態的任務會在父任務完成後自動執行。

      在任務內部由TaskScheduler類調度任務的執行,該類是一個抽象類別,FCL中從他派生了兩個衍生類別:ThreadPoolTaskScheduler線程池任務調度器和SynchronizationContextTaskScheduler同步上下文任務調度器。所有任務預設都是採用ThreadPoolTaskScheduler調度任務,他是採用線程池來執行任務,可以通過TaskScheduler類的靜態屬性Default獲得對預設任務調度器的引用。SynchronizationContextTaskScheduler任務調度器能夠用在Window form、WPF等應用程式,他的任務調度是採用的GUI線程,所以他能同步更新UI組件,可以通過TaskScheduler類的靜態方法FromCurrentSynchronizationContext獲得對一個同步上下文任務調度起的引用。

任務調度樣本:

   1:    private void button1_Click(object sender, EventArgs e)
   2:          {
   3:               //獲得同步上下文任務調度器
   4:             TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
   5:   
   6:              //建立任務,並採用預設任務調度器(線程池任務調度器)執行任務
   7:              Task<int> task = new Task<int>(() =>
   8:              {
   9:                  //執行複雜的計算任務。
  10:                  Thread.Sleep(2000);
  11:                  int sum = 0;
  12:                  for (int i = 0; i < 100; i++)
  13:                  {
  14:                      sum += i;
  15:                  }
  16:                  return sum;
  17:              });
  18:               var cts=new CancellationTokenSource();
  19:              //任務完成時啟動一個後續任務,並採用同步上下文任務調度器調度任務更新UI組件。
  20:              task.ContinueWith(t => {this.label1.Text="採用SynchronizationContextTaskScheduler任務調度器更新UI。\r\n計算結果是:"+task.Result.ToString(); },
  21:                 cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
  22:              task.Start();
  23:          }

執行結果:

    本文簡單的介紹了使用Task類來執行非同步作業以及任務的內部實現與任務調度。在執行複雜非同步作業時,可以採用任務來執行,他能更好的知道非同步作業在何時完成以及返回非同步作業的執行結果。

轉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.