1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        .NET 中異步任務的取消和監(jiān)控

        共 8662字,需瀏覽 18分鐘

         ·

        2021-09-19 11:52



        相關類型


        CancellationTokenSource 主要用來創(chuàng)建或取消令牌


        CancellationToken 監(jiān)聽令牌狀態(tài),注冊令牌取消事件


        OperationCanceledException 令牌被取消時拋出的異常,可以由監(jiān)聽者自主決定是否拋出異常


        CancellationTokenSource


        創(chuàng)建令牌:

        CancellationTokenSource cts = new CancellationTokenSource()

        CancellationToken token=cts.Token;

        取消釋放令牌:

        cts.Cancel();

        CancellationToken

        監(jiān)聽令牌取消事件:

        token.Register(() => Console.WriteLine("令牌被取消"));

        判斷令牌是否取消

        //返回一個bool,如果令牌被取消為true
        token.IsCancellationRequested

        //如果token被取消則拋出異常,內(nèi)部實現(xiàn)其實就是判斷IsCancellationRequested
        token.ThrowIfCancellationRequested()=>{
         if(token.IsCancellationRequested){
          throw new OperationCanceledException();
         }
        }

        代碼示例

        下面模擬一個文件下載的任務,在未下載完成后下載任務被取消

         public void Run()
         {
             CancellationTokenSource cts = new CancellationTokenSource();

             Task.Run(() =>
                      {
                          //等待兩秒后取消,模擬的是用戶主動取消下載任務
                          Thread.Sleep(2000);
                          cts.Cancel();
                      });

             try
             {
                 var size = DownloadFile(cts.Token);
                 Console.WriteLine("文件大?。? + size);
             }
             catch (OperationCanceledException)
             {
                 Console.WriteLine("下載失敗");
             }finally{
                 cts.Dispose();
             }
             Thread.Sleep(2000);
         }


        /// <summary>
        /// 模擬下載文件,下載文件需要五秒
        /// </summary>
        /// <returns></returns>
        public int DownloadFile(CancellationToken token)
        {
            token.Register(() =>
                           {
                               System.Console.WriteLine("監(jiān)聽到取消事件");
                           });

            Console.WriteLine("開始下載文件");
            for (int i = 0; i < 5; i++)
            {
                token.ThrowIfCancellationRequested();
                Console.WriteLine(i.ToString());
                Thread.Sleep(1000);
            }
            Console.WriteLine("文件下載完成");
            return 100;
        }

        輸出結果:

        開始下載文件
        0
        1
        監(jiān)聽到取消事件
        下載失敗

        思考

        為什么要將CancellationToken和CancellationTokenSource分為兩個類呢,直接一個CancellationToken又可以取消又可以判斷狀態(tài)注冊啥的不是更好,更方便?

        其實每種類的設計和實現(xiàn)都可以有很多不同的策略,CTS和CT從這個兩個類提供的為數(shù)不多的公開方法中就可以看出,CTS用來控制Token的生成和取消等生命周期狀態(tài),CT只能用來監(jiān)聽和判斷,無法對Token的狀態(tài)進行改變。

        所以這種設計的目的就是關注點分離。限制了CT的功能,避免Token在傳遞過程中被不可控的因素取消造成混亂。

        關聯(lián)令牌

        繼續(xù)拿上面的示例來說,示例中實現(xiàn)了從外部控制文件下載功能的終止。

        如果要給文件下載功能加一個超時時間的限制,此時可以增加一個控制超時時間的token,將外部傳來的token和內(nèi)部token 關聯(lián)起來變?yōu)橐粋€token

        只需要將DownloadFile()函數(shù)做如下改造即可

        public int DownloadFile(CancellationToken externalToken)
                {
                    //通過構造函數(shù)設置TokenSource一秒之后調(diào)用Cancel()函數(shù)
                    var timeOutToken = new CancellationTokenSource(new TimeSpan(0, 0, 1)).Token;
                    using (var linkToken = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeOutToken))
                    {
                        Console.WriteLine("開始下載文件");
                        for (int i = 0; i < 5; i++)
                        {
                            linkToken.Token.ThrowIfCancellationRequested();
                            Console.WriteLine(i.ToString());
                            Thread.Sleep(1000);
                        }
                        Console.WriteLine("文件下載完成");
                        return 100;
                    }
                }

        此時不論是externalToken取消,或是timeOutToken取消,都會觸發(fā)linkToken的取消事件

        CancellationChangeToken

        CancellationChangeToken主要用來監(jiān)測目標變化,需配合ChangeToken使用。從功能場景來說,其實ChangeToken的功能和事件似乎差不多,當監(jiān)控的目標發(fā)生了變化,監(jiān)聽者去做一系列的事情。

        但是事件的話,監(jiān)聽者需要知道目標的存在,就是如果A要注冊B的事件,A是要依賴B的。

        CancellationChangeToken是基于CancellationToken來實現(xiàn)的,可以做到依賴于Token而不直接依賴被監(jiān)聽的類

        創(chuàng)建CancellationChangeToken:

        new CancellationChangeToken(new CancellationTokenSource().Token)

        監(jiān)聽Token變動

        new CancellationChangeToken(cts.Token).RegisterChangeCallback(obj => Console.WriteLine("token 變動"), null);

        CancellationChangeToken只是把CancellationToken包裝了一層。RegisterChangeCallback最終也是監(jiān)聽的CancellationToken的IsCancellationRequested狀態(tài)。

        所以就有個問題,代碼寫到這里,并不能實現(xiàn)每次內(nèi)部變動都觸發(fā)回調(diào)事件。

        因為CT只會Cancel一次,對應的監(jiān)聽也會執(zhí)行一次。無法實現(xiàn)多次監(jiān)聽

        為了實現(xiàn)變化的持續(xù)監(jiān)聽,需要做兩個操作

        • 讓Token在Cancel之后重新初始化
        • 每次Cancel回調(diào)之后重新監(jiān)聽新的Token

        先上代碼,下面的代碼實現(xiàn)了每次時間變動都會通知展示面板刷新時間的顯示

        public void Run()
        {
            var bjDate = new BeijingDate();
            DisplayDate(bjDate.GetChangeToken, bjDate.GetDate);
            Thread.Sleep(50000);
        }

        public void DisplayDate(Func<IChangeToken> getChangeToken, Func<DateTime> getDate)
        {
            ChangeToken.OnChange(getChangeToken, () => Console.WriteLine("當前時間:" + getDate()));
        }

        public class BeijingDate
        {
            private CancellationTokenSource cts;
            private DateTime date;
            public BeijingDate()
            {
                cts = new CancellationTokenSource();
                var timer = new Timer(TimeChange, null01000);
            }

            private void TimeChange(object state)
            {
                date = DateTime.Now;
                var old = cts;
                cts = new CancellationTokenSource();
                old.Cancel();
            }

            public DateTime GetDate() => date;
            public CancellationChangeToken GetChangeToken()
            {
                return new CancellationChangeToken(cts.Token);
            }
        }

        在TimeChange()中修改了時間,重置了Token并將舊的Token取消

        DisplayDate中用ChangeToken.OnChange獲取對應的Token并監(jiān)聽

        實現(xiàn)了DisplayData函數(shù)和BeijingDate這個類的解耦

        ChangeToken.OnChange 這個函數(shù)接收兩個參數(shù),一個是獲取Token的委托,一個是Token取消事件的響應委托。

        每次在處理完Token的取消事件后,他會重新調(diào)用第一個委托獲取Token,而此時我們已經(jīng)生成了新的Token,最終實現(xiàn)了持續(xù)監(jiān)控

        轉(zhuǎn)自:張三~~

        鏈接:cnblogs.com/bluesummer/p/15219701.html

        - EOF -

        瀏覽 43
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            特级西西444www大胆免费看 | 国产老熟女性爱视频 | 国语精品| 国精自拍 | 久久成人三级电影 | 校花一区二区三区 | 自拍超碰在线观看 | 日韩一级爱爱 | 将军边走边挺进她的h经典语录 | 精品国产 国产没封 |