1. 程式人生 > >CLR via C# 筆記 ----Task(任務) 1

CLR via C# 筆記 ----Task(任務) 1

直接給執行緒池新增工作項的方式很直接,但是無法得知工作項何時結束,並且不能獲得返回值

於是,CLR提供了一個Task類封裝了工作項

以下三種寫法某種意義上來說是等價的

  //1作為object傳遞(僅僅是舉例的作用),當回撥需求一個ojbect引數時,需要額外傳遞這個引數
        ThreadPool.QueueUserWorkItem(DoSth,1);
        new Task(DoSth, 1).Start();
        Task.Run(()=>DoSth(1));

 public void DoSth(object o) {  /*DoSth*/}

這三種寫法都無法獲取返回值,只是為了展示Task和執行緒池工作項之間的關係

實際的Task用法可能是這樣

 public int DoSth()
    {
        //做一些耗時工作
        Thread.Sleep(2000);
        //返回一個結果
        return 1;
    } 
   Task<int> task1 = new Task<int>(DoSth);
  //task建立好後,可以在任意時刻start
   task1.Start();
  //呼叫task的Result屬性 獲取返回值 通常還需要try catch處理 先略過
   Console.WriteLine(task1.Result);

很好,可以獲取工作項的返回值了,但是這種獲取方式有問題,直接呼叫task的Result屬性 會造成呼叫執行緒阻塞 (需要等待工作項完成)

這和多執行緒使用的初衷根本不相符。

所以Task有一個方法用來註冊任務完成時的回撥(延續任務),引數是task本身

 task1.ContinueWith(t => Console.WriteLine(t.Result));

呼叫這個方法不會造成任何阻塞,而傳進去的回撥 在工作項結束後會被另一個執行緒執行

同時,還可額外傳遞一個TaskContinuationOptions型別的列舉,來指定此延續任務的執行方式

  [Flags]
    public enum TaskContinuationOptions
    {
        None = 0,//預設
        PreferFairness = 1,//提意快速執行 (因為延續任務本質上還是任務,會由執行緒池的排程器排程)
        LongRunning = 2,//書中描述 提意由儘可能由執行緒池執行緒執行 (但上文又說延續任務都由執行緒池負責...)
        AttachedToParent = 4,//將延續任務作為上一個任務的子任務
        DenyChildAttach = 8,//拒絕Attach (以此延續任務為父任務)
        HideScheduler = 16,//啟用預設排程器 (任務可以指定一個排程器,預設情況下,延續任務使用上一個任務的排程器)
        LazyCancellation = 32,//上一個任務被取消了的話,本延續任務取消
        RunContinuationsAsynchronously = 64,//用其他執行緒執行延續任務
        NotOnRanToCompletion = 65536,
        NotOnFaulted = 131072,
        OnlyOnCanceled = 196608,//只有在上一個任務取消的時候 才執行
        NotOnCanceled = 262144,
        OnlyOnFaulted = 327680,//上一個任務異常時才執行
        OnlyOnRanToCompletion = 393216,//上一個任務順利完成時才執行
        ExecuteSynchronously = 524288//同步執行延續任務(但不阻塞 原理不明..)
    }

如果不指定標誌,延續任務總會執行

最後提一嘴子任務,子任務就是在任務的函式中 又建立的任務,並且在TaskCreationOptions.AttachedToParent(注意這個列舉和上面那個不同...列舉中的標誌部分相同,畢竟本質上都是在建立一個任務)(預設任何地方建立的任務都是頂級任務,除非指定標誌)

父任務需要等待子任務全部完成才能算完成。