<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 8662字,需瀏覽 18分鐘

           ·

          2021-09-19 11:52



          相關(guān)類型


          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();
           }
          }

          代碼示例

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

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

               Task.Run(() =>
                        {
                            //等待兩秒后取消,模擬的是用戶主動取消下載任務(wù)
                            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;
          }

          輸出結(jié)果:

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

          思考

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

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

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

          關(guān)聯(lián)令牌

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

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

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

          public int DownloadFile(CancellationToken externalToken)
                  {
                      //通過構(gòu)造函數(shù)設(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)測目標(biāo)變化,需配合ChangeToken使用。從功能場景來說,其實ChangeToken的功能和事件似乎差不多,當(dāng)監(jiān)控的目標(biāo)發(fā)生了變化,監(jiān)聽者去做一系列的事情。

          但是事件的話,監(jiān)聽者需要知道目標(biāo)的存在,就是如果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一次,對應(yīng)的監(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("當(dāng)前時間:" + 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獲取對應(yīng)的Token并監(jiān)聽

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

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

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

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

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

          - EOF -

          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人污污污 | 久久人妻无码中文字幕系列 | 波多野结衣性爱视频 | 网红操逼视频在线观看免费视频在线观看 | 91成人在线免费视频 |