1. 程式人生 > >線程池和任務

線程池和任務

pool call public 異常 token 操作系統 null deb gets

  1. 線程池

    1. 線程池基礎
      • 創建線程和銷毀線程是一個昂貴的操作,要耗費大量的時間。由於操作系統必須調度可運行的線程並執行上線文切換,所以太多的線程還對性能不利。 為了改善這個情況,clr包含了代碼來管理他自己的線程池。 線程池是你的應用程序能使用的線程集合。 線程池內部會維護一個 操作請求隊列。應用程序執行一個異步請求操作時,將一個記錄項(entry)追加到線程池的隊列中。線程池的代碼從這個對立中 提取記錄項,將這個記錄項派發(dispatch)給一個線程池線程。 當線程池完成任務後,線程不會被銷毀。相反,線程會回到線程池,在哪裏進入空閑狀態,等待相應另一個請求。由於線程不銷毀自身,所以不在再產生額技術分享
        外的性能損失;
      • 技術分享
    2. 我們來演示以線程池的方式異步的調用一個方法
      public static void MainThreadPool()
              {
                  Console.WriteLine("主線程異步操作隊列");
                  ThreadPool.QueueUserWorkItem(ThreadProc);
      
                  Console.WriteLine("主線程做其他工作");
                  Thread.Sleep(1000);//模擬其他工作
      
                  Console.WriteLine(
      "主線程運行結束"); Console.ReadLine(); } /// <summary> /// 與委托WaitCallback 簽名相同的回掉方法 /// </summary> /// <param name="stateInfo">如果使用的是QueueUserWorkItem(WaitCallback callBack),該參數默認為null</param> static void ThreadProc(Object stateInfo) { Console.WriteLine(
      "線程池工作線程執行回掉"); Thread.Sleep(1000); //模擬其他工作 }

      輸出:技術分享

  1. 任務

    1. 很容易使用ThreadPoolQueueUserWorkItem方法發起一次異步的計算限制操作。但這個技術有許多限制。最大的問題是沒有內置的機制 讓你知道操作在什麽時候完成,也沒有機制在操作完成時得到返回值。為了克服這些限制(並解決其他一些問題),微軟引入了任務的概念。 通過using System.Threading.Tasks 命名空間中的類型來使用任務。
    2. 創建任務
      public static void RunGetStartTask()
              {
                  //創建方式之一 :通過task的構造函數
                  Task t1 = new Task(Task1);
                  //創建方式之二:通過task工廠調用StartNew 創建並並啟用
                  Task t2 = Task.Factory.StartNew(Task2);
      
                  Console.WriteLine("t1任務Start之前狀態:{0},t2任務狀態:{1}", t1.Status, t2.Status);
                  t1.Start();
                  Console.WriteLine("t1任務Start之後狀態:{0},t2任務狀態:{1}", t1.Status, t2.Status);
                  
                  Task.WaitAll(t1, t2);
      
                  Console.WriteLine("主線程執行完畢");
                  Console.WriteLine("t1最終狀態:{0},t2最終狀態:{1}", t1.Status, t2.Status);
                  Console.ReadLine();
              }
              public static void Task1()
              {
                  Thread.Sleep(1000);
                  Console.WriteLine("運行task1");
              }
              public static void Task2()
              {
                  Thread.Sleep(2000);
                  Console.WriteLine("運行task2");
              }

      輸出技術分享

    3. 理解任務狀態和生命周期
      • task實例的執行取決於底層硬件和運行時可用的資源。因此,在您獲取了有關task實例的任何信息後,這個狀態可能會發生改變,因為task實例的狀態也在同時發生改變。當task到達它的3種可能的最終之一時,它再也回不去之前的任何狀態了,如圖所示。技術分享
      • T初始狀態:task實例有3種可能的初始狀態,詳細說明如圖技術分享
      • 最終狀態:接下來,task實例可以轉換到running狀態,並且最終轉變到一個最終狀態。 如果task實例有關聯的子任務,那麽就不能認為這個task完成了,並且這個task將轉變到WaitingForChildrenToComplete 狀態。當task實例的子任務都完成後,這個Task將進入3種可能的最終狀態之一。詳細說明如下如圖
      • 技術分享
    4. 取消任務
      • 如果想中斷task實例的執行,可以通過取消標記(Cancellation Token)
      • CancellationTokenSource 能夠初始化取消的請求,而CancellationToken能夠將這些請求傳遞給異步的操作。 
    5. public static void RunGetStartTaskCancel()
              {
                  var cts = new CancellationTokenSource();
                  var ct = cts.Token;
      
                  var sw = Stopwatch.StartNew();
                  var t1 = Task.Factory.StartNew(() => Task1Cancel(ct), ct);
      
                  var t2 = Task.Factory.StartNew(() => Task2Cancel(ct), ct);
                  //主線程模擬1秒
                  Thread.Sleep(1000);
      
                  cts.Cancel();
      
                  try
                  {
                      if (!Task.WaitAll(new Task[] { t1, t2 }, 3000))
                      {
                          Console.WriteLine("Task1Cancel 和 Task2Cancel 超過了2秒完成.");
                          Console.WriteLine("t1狀態" + t1.Status.ToString());
                          Console.WriteLine("t2狀態" + t2.Status.ToString());
                          Console.ReadLine();
                      }
                      Console.ReadLine();
      
                  }
                  catch (AggregateException ex)
                  {
                      foreach (Exception innerException in ex.InnerExceptions)
                      {
                          Debug.WriteLine(innerException.ToString());
                          Console.WriteLine("異常消息:" + innerException.ToString());
                      }
      
                      if (t1.IsCanceled)
                      {
                          Console.WriteLine("t1 task 運行Task1Cancel 已取消");
                      }
      
                      if (t1.IsCanceled)
                      {
                          Console.WriteLine("t2 task 運行Task2Cancel 已取消");
                      }
      
                      Console.WriteLine("耗時" + sw.Elapsed.ToString());
                      Console.WriteLine("完成");
                      Console.ReadLine();
      
                  }
              }
              public static void Task1Cancel(CancellationToken ct)
              {
                  ct.ThrowIfCancellationRequested();
                  var sw = Stopwatch.StartNew();
                  Thread.Sleep(1000); 
      
                  Console.WriteLine("運行task1");
                  Console.WriteLine("task1:" + sw.Elapsed.ToString());
                  ct.ThrowIfCancellationRequested();
              }
              public static void Task2Cancel(CancellationToken ct)
              {
                  ct.ThrowIfCancellationRequested();
                  var sw = Stopwatch.StartNew();
                  Thread.Sleep(2000);
      
                  Console.WriteLine("運行task2");
                  Console.WriteLine("task2:" + sw.Elapsed.ToString());
              }
    6. 從任務獲取返回值
      • 目前,我們的任務實例還沒有返回值,他們都是運行一些不返回值的委托。正如開頭將的,任務是可以有返回值的。通過使用Task<TResult>實例,其中TResult要替換為返回類型。
        public static void RunGetStartTaskResult()
                {
                    var t1 = Task.Factory.StartNew(() => GetTask1("ChengTian"));
                    //等待t1 完成
                    t1.Wait();
                    var t2 = Task.Factory.StartNew(() =>
                    {
                        for (int i = 0; i < t1.Result.Count; i++)
                        {
                            Console.WriteLine(t1.Result[i]);
                        }
        
                    });
        
                    Console.WriteLine("結束");
                    Console.ReadLine();
                }
        
                public static List<char> GetTask1(string ss)
                {
                    Thread.Sleep(1000);
                    return ss.ToList();
                }

        輸出返回值 技術分享

    7. 通過延續串聯任務
      • 改造下上面的例子,在t1任務完成之後啟動t2任務
    8. public static void RunGetStartTaskContinueWith()
              {
                  var t1 = Task.Factory.StartNew(() => GetTask1("ChengTian"));
                  var t2 = t1.ContinueWith(t =>
                  {
                      for (int i = 0; i < t1.Result.Count; i++)
                      {
                          Console.WriteLine(t1.Result[i]);
                      }
      
                  });
      
                  Console.WriteLine("結束");
                  Console.ReadLine();
              }

      可以在任何任務實例上調用ContinueWith方法,創建一個延續。 你也可以串聯很多任務,然後等待最後一個任務(在這個實例中為t2)執行完成.

線程池和任務