C# 多線程學習系列四之取消、超時子線程操作

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方法添加回調函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章