CLR via C# 筆記 ----Task(任務) 1
阿新 • • 發佈:2019-01-12
直接給執行緒池新增工作項的方式很直接,但是無法得知工作項何時結束,並且不能獲得返回值
於是,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(注意這個列舉和上面那個不同...列舉中的標誌部分相同,畢竟本質上都是在建立一個任務)(預設任何地方建立的任務都是頂級任務,除非指定標誌)
父任務需要等待子任務全部完成才能算完成。