1、簡介
雖然ThreadPool、Thread能開啓子線程將一些任務交給子線程去承擔,但是很多時候,因爲某種原因,比如子線程發生異常、或者子線程的業務邏輯不符合我們的預期,那麼這個時候我們必須關閉它,而不是讓它繼續執行,消耗資源.讓CPU不在把時間和資源花在沒有意義的代碼上.
2、主線程取消所有子線程執行的簡單代碼演示和原理分析
(1)、代碼演示
static void Main(string[] args) { //顯示定義一個取消輔助線程的操作 CancellationTokenSource ctsToken = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(o => EoworkOne(ctsToken.Token)); ThreadPool.QueueUserWorkItem(o => EoworkTwo(ctsToken.Token)); ctsToken.Cancel(); Console.Read(); } /// <summary> /// 輔助線程一 /// </summary> /// <param name="token"></param> static void EoworkOne(CancellationToken token) { //判斷主線程是否調用了CancellationTokenSource實例的Cancel方法 //相當於判斷主線程是否傳遞給輔助線程一一個取消標記 if (token.IsCancellationRequested) { //如果主線程傳遞給輔助線程一一個取消操作標記,執行下面的代碼 Console.WriteLine("主線程調用了Cancel方法,所以輔助線程一獲取了主線程取消輔助線程一的標記,但是並不會真正的關閉當前線程"); Console.WriteLine("輔助線程一執行return操作,自己顯示的退出,那麼接下去的方法都不會被執行"); return; } } /// <summary> /// 輔助線程二 /// </summary> /// <param name="token"></param> static void EoworkTwo(CancellationToken token) { //判斷主線程是否調用了CancellationTokenSource實例的Cancel方法 //相當於判斷主線程是否傳遞給輔助線程一一個取消標記 if (token.IsCancellationRequested) { //如果主線程傳遞給輔助線程一一個取消操作標記,執行下面的代碼 Console.WriteLine("主線程調用了Cancel方法,所以輔助線程二獲取了主線程取消輔助線程二的標記,但是並不會真正的關閉當前線程"); } //因爲當主線程傳遞給輔助線程二一個取消標記,但是上面的if語句塊,並沒有執行return操作,所以下面的語句還是會繼續執行 Console.WriteLine("輔助線程二獲得取消標記操作後,並沒有執行顯示的return操作,所以輔助線程二繼續執行"); }
(2)、原理分析
第一步:創建一個CancellationTokenSource對象實例,該對象包含了所有關於取消子線程有關的所有狀態
CancellationTokenSource ctsToken = new CancellationTokenSource();
第二步:將CancellationTokenSource對象實例的CancellationToken對象實例傳遞給需要進行取消操作的所有子線程.並且可以通過這個CancellationToken對象實例關聯到CancellationTokenSource對象實例.
ThreadPool.QueueUserWorkItem(o => EoworkOne(ctsToken.Token)); ThreadPool.QueueUserWorkItem(o => EoworkTwo(ctsToken.Token));
第三步:當主線程調用CancellationTokenSource對象實例的Cancel方法,所有的子線程通過調用CancellationToken對象實例的IsCancellationRequested屬性,該屬性定時去獲取初始線程(主線程)是否執行了CancellationTokenSource對象實例的Cancel方法,如果調用了,該屬性爲true。這時可以理解爲子線程到主線程的取消信號,可以通過調用return方法來終止子線程的操作.
//判斷主線程是否調用了CancellationTokenSource實例的Cancel方法 //相當於判斷主線程是否傳遞給輔助線程一一個取消標記 if (token.IsCancellationRequested) { //如果主線程傳遞給輔助線程一一個取消操作標記,執行下面的代碼 Console.WriteLine("主線程調用了Cancel方法,所以輔助線程一獲取了主線程取消輔助線程一的標記,但是並不會真正的關閉當前線程"); Console.WriteLine("輔助線程一執行return操作,自己顯示的退出,那麼接下去的方法都不會被執行"); return; }
3、如果創建一個不能被取消的子線程
通過給子線程傳遞一個CancellationToken.None實例,該子線程無法被取消,原因很簡單,CancellationToken.None實例沒有關聯的CancellationTokenSource對象實例,所以無法調用Cancel方法顯示取消.所以子線程調用token.IsCancellationRequested屬性,該屬性永遠爲false.調用token.CanBeCanceled屬性也爲false.
static void Main(string[] args) { ThreadPool.QueueUserWorkItem(o => EoworkOne(CancellationToken.None)); Console.Read(); } /// <summary> /// 輔助線程一 /// </summary> /// <param name="token"></param> static void EoworkOne(CancellationToken token) { if (token.IsCancellationRequested) { //永遠無法執行 } Console.WriteLine("輔助線程一能被取消嗎?{0}",token.CanBeCanceled?"能":"不能"); Console.WriteLine("通過CancellationToken.None實例創建的子線程無法被取消"); }
4、初始線程(主線程)調用給CancellationTokenSource對象實例的Cancel方法添加回調函數