線程池任務
阿新 • • 發佈:2017-07-18
reac isp 集合 貴的 png 信息 異步操作 cell 構造函數
線程池和任務
-
線程池
- 線程池基礎
- 創建線程和銷毀線程是一個昂貴的操作,要耗費大量的時間。由於操作系統必須調度可運行的線程並執行上線文切換,所以太多的線程還對性能不利。
為了改善這個情況,clr包含了代碼來管理他自己的線程池。
線程池是你的應用程序能使用的線程集合。
線程池內部會維護一個 操作請求隊列。應用程序執行一個異步請求操作時,將一個記錄項(entry)追加到線程池的隊列中。線程池的代碼從這個對立中
提取記錄項,將這個記錄項派發(dispatch)給一個線程池線程。
當線程池完成任務後,線程不會被銷毀。相反,線程會回到線程池,在哪裏進入空閑狀態,等待相應另一個請求。由於線程不銷毀自身,所以不在再產生額
- 創建線程和銷毀線程是一個昂貴的操作,要耗費大量的時間。由於操作系統必須調度可運行的線程並執行上線文切換,所以太多的線程還對性能不利。
為了改善這個情況,clr包含了代碼來管理他自己的線程池。
線程池是你的應用程序能使用的線程集合。
線程池內部會維護一個 操作請求隊列。應用程序執行一個異步請求操作時,將一個記錄項(entry)追加到線程池的隊列中。線程池的代碼從這個對立中
提取記錄項,將這個記錄項派發(dispatch)給一個線程池線程。
當線程池完成任務後,線程不會被銷毀。相反,線程會回到線程池,在哪裏進入空閑狀態,等待相應另一個請求。由於線程不銷毀自身,所以不在再產生額
- 我們來演示以線程池的方式異步的調用一個方法
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); //模擬其他工作 }
輸出:
- 線程池基礎
-
任務
- 很容易使用ThreadPool的QueueUserWorkItem方法發起一次異步的計算限制操作。但這個技術有許多限制。最大的問題是沒有內置的機制 讓你知道操作在什麽時候完成,也沒有機制在操作完成時得到返回值。為了克服這些限制(並解決其他一些問題),微軟引入了任務的概念。 通過using System.Threading.Tasks 命名空間中的類型來使用任務。
- 創建任務
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"); }
輸出
- 理解任務狀態和生命周期
- task實例的執行取決於底層硬件和運行時可用的資源。因此,在您獲取了有關task實例的任何信息後,這個狀態可能會發生改變,因為task實例的狀態也在同時發生改變。當task到達它的3種可能的最終之一時,它再也回不去之前的任何狀態了,如圖所示。
- T初始狀態:task實例有3種可能的初始狀態,詳細說明如圖
- 最終狀態:接下來,task實例可以轉換到running狀態,並且最終轉變到一個最終狀態。 如果task實例有關聯的子任務,那麽就不能認為這個task完成了,並且這個task將轉變到WaitingForChildrenToComplete 狀態。當task實例的子任務都完成後,這個Task將進入3種可能的最終狀態之一。詳細說明如下如圖
- 取消任務
- 如果想中斷task實例的執行,可以通過取消標記(Cancellation Token)
-
CancellationTokenSource 能夠初始化取消的請求,而CancellationToken能夠將這些請求傳遞給異步的操作。
-
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()); }
- 從任務獲取返回值
- 目前,我們的任務實例還沒有返回值,他們都是運行一些不返回值的委托。正如開頭將的,任務是可以有返回值的。通過使用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(); }
輸出返回值
- 目前,我們的任務實例還沒有返回值,他們都是運行一些不返回值的委托。正如開頭將的,任務是可以有返回值的。通過使用Task<TResult>實例,其中TResult要替換為返回類型。
- 通過延續串聯任務
- 改造下上面的例子,在t1任務完成之後啟動t2任務
-
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)執行完成.
線程池任務