任務

  任務表示某個工做單元。這個工做單元能夠在單獨的線程中運行,也能夠同步方式啓動一個任務,使主線程等待。任務能夠在完成後定義一個連續工做,同時也能夠有子任務(但取消父任務後,子任務也會被取消)。安全

一、啓動任務

  使用TaskFactory類或Task類的構造方法和Start()方法。啓動任務時,會建立一個Task類的實例,利用Action或者Action<object>委託,能夠指定應該運行的代碼。建立一個方法做爲後面線程調用的代碼:函數

static void TaskMethod(object title)
{
    lock (taskMethodLock)
    {
        Console.WriteLine(title);
        Console.WriteLine("Task id: {0}, thread: {1}",
          Task.CurrentId == null ? "no task" : Task.CurrentId.ToString(),
          Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("is pooled thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
        Console.WriteLine("is background thread: {0}", Thread.CurrentThread.IsBackground);
        Console.WriteLine();
    }
}

1.一、使用線程池的任務

  建立任務有四種方式: oop

static void TasksUsingThreadPool()
{
    TaskFactory tf = new TaskFactory();
    Task t1 = tf.StartNew(TaskMethod, "using a task factory");

    Task t2 = Task.Factory.StartNew(TaskMethod, "factory via a task");

    Task t3 = new Task(TaskMethod, "using a task constructor and Start");
    t3.Start();

    Task t4 = Task.Run(() => TaskMethod("using the Run method"));
}

  使用Task構造函數和TaskFactory的StartNew方法時,能夠傳遞TaskCreationOptions的枚舉值。它能夠改變任務的行爲。this

1.二、同步任務

  任務能夠同步運行,以相同的線程做爲主調線程。使用Task的RunSynchronously方法:spa

private static void RunSynchronousTask()
{
    TaskMethod("just the main thread");
    var t1 = new Task(TaskMethod, "run sync");
    t1.RunSynchronously();
}

  首先TaskMethod方法在主線程調用,而後在新建立的Task上調用。運行代碼,從輸出能夠看出,主線程是一個前臺線程。沒有任務ID,也不是線程池的線程。調用RunSyncronously方法時,會使用相同的線程做爲主調線程,可是若是之前沒有建立任務,就會建立一個新任務。線程

1.三、使用單獨線程的任務

  若是任務須要長時間運行,那麼使用TaskCreationOptions.LongRunning告訴任務調度器建立一個新的線程,而不是使用線程池中的線程。新線程將不受線程池管理。若是線程來自於線程池,任務調度器能夠決定等待已經運行的任務完成,而後使用這個線程,而不是在線程中建立一個新的線程。對於長時間運行的線程,任務調度器會當即知道不能等待它們完成。調試

二、Futrue——任務的結果

  任務結束時,能夠把一些有用的信息寫入共享對象中,但共享對象必須是線程安全的。另外一種方法是使用返回某個結果的任務:code

static void ResultsFromTasks()
{
    //TResult是後面Func的返回值,第二個參數是Func的傳入參數
    Task<Tuple<int, int>> 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);
}

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);
}

三、連續任務

  能夠指定在任務完成後,應該開始運行的另外一個特定任務。如:一個任務失敗後,那麼任務的後續工做就是清理工做。任務處理程序不帶參數,或者帶一個object類型的參數,連續處理程序有一個Task類型的參數,能夠訪問起始任務的相關信息。對象

 static void ContinuationTask()
 {
     Task t1 = new Task(DoOnFirst);
     //連續任務經過在任務上調用ContinueWith方法定義,也可使用TaskFactory類。
     Task t2 = t1.ContinueWith(DoOnSecond);//調用DoOnSecond方法的新任務在任務t1結束後當即啓動。
     Task t3 = t1.ContinueWith(DoOnSecond);//在一個任務結束時,能夠啓動多個任務
     Task t4 = t2.ContinueWith(DoOnSecond);//連續任務也能夠另外一個連續任務
     //不管前一個任務是如何結束的,前面的連續任務都會在前一個任務結束時啓動
     //使用TaskContinuationOptions的枚舉值,能夠指定連續任務啓動的前提條件(前一個任務的狀態)
     Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);
     t1.Start();
     Thread.Sleep(5000);
 }

 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);
 }

 static void DoOnError(Task t)
 {
     Console.WriteLine("task {0} had an error!", t.Id);
     Console.WriteLine("my id {0}", Task.CurrentId);
     Console.WriteLine("do some cleanup");
 }

四、任務層次結構

  利用任務的連續性,能夠在一個任務結束後啓動另外一個任務。任務也能夠構成一個層次結構,一個任務啓動一個新任務時,就啓動了一個父/子層次結構。blog

 static void ParentAndChild()
 {
     Task 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。取消父任務,也會取消子任務。

//:啓動父/子關係結構任務的另外一種方法
static void ParentAndChild()
{
    TaskFactory factory = new TaskFactory();
    Task parent = factory.StartNew(() =>
       {
           Console.WriteLine("parent task {0}", Task.CurrentId);

           Task child = factory.StartNew(() =>
           {
               Console.WriteLine("child task {0}", Task.CurrentId);
               Thread.Sleep(2000);
               Console.WriteLine("finished child");
           }, TaskCreationOptions.AttachedToParent);

           Console.WriteLine("finished parent");
       });
    parent.Wait();
}

五、任務的取消

  

static void CancelTask()
{
    var cts = new CancellationTokenSource();//也可使用Task.Factory.CancellationToken(若是僅須要一個取消標記)
    cts.Token.Register(() => Console.WriteLine("*** task cancelled"));//在發生取消標記時調用。
    cts.CancelAfter(500);            //500ms後取消任務
    Task t1 = Task.Run(() =>
      {
          Console.WriteLine("in task");
          for (int i = 0; i < 20; i++)
          {
              Thread.Sleep(100);
              CancellationToken token = cts.Token;
              if (token.IsCancellationRequested)
              {
                  Console.WriteLine("cancelling was requested, cancelling from within the task");
                  token.ThrowIfCancellationRequested();//注意:運行調試程序會在此處報異常,運行exe會在下面捕捉到異常
                  break;
              }
              Console.WriteLine("in loop");
          }
          Console.WriteLine("task finished without cancellation");
      }, cts.Token);

    try
    {
        t1.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine("exception: {0}, {1}", ex.GetType().Name, ex.Message);
        foreach (var innerException in ex.InnerExceptions)
        {
            Console.WriteLine("inner excepion: {0}, {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
        }
    }
}
相關文章
相關標籤/搜索