<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>

          C#異步編程由淺入深:Async/Await的作用

          共 24091字,需瀏覽 49分鐘

           ·

          2021-08-16 06:19


          考慮到直接講實(shí)現(xiàn)一個(gè)類Task庫(kù)思維有點(diǎn)跳躍,所以本節(jié)主要講解Async/Await的本質(zhì)作用(解決了什么問題),以及Async/Await的工作原理。實(shí)現(xiàn)一個(gè)類Task的庫(kù)則放在后面講。

              class Program
              {

                  public static string GetMessage()
                  {
                      return Console.ReadLine();
                  }

                  public static  string TranslateMessage(string msg)
                  {
                      return msg;
                  }

                  public static  void DispatherMessage(string msg)
                  {
                      switch (msg)
                      {
                          case "MOUSE_MOVE":
                              {
                                  OnMOUSE_MOVE(msg);
                                  break;
                              }
                          case "MOUSE_DOWN":
                              {
                                  OnMouse_DOWN(msg);
                                  break;
                              }
                          default:
                              break;
                      }
                  }

                  public static void OnMOUSE_MOVE(string msg)
                  {
                      Console.WriteLine("開始繪制鼠標(biāo)形狀");
                  }


                  public static int Http()
                  {
                      Thread.Sleep(1000);//模擬網(wǎng)絡(luò)IO延時(shí)
                      return 1;
                  }
                  public static void HttpAsync(Action<int> action,Action error)
                  {
                      //這里我們用另一個(gè)線程來實(shí)現(xiàn)異步IO,由于Http方法內(nèi)部是通過Sleep來模擬網(wǎng)絡(luò)IO延時(shí)的,這里也只能通過另一個(gè)線程來實(shí)現(xiàn)異步IO
                      //但記住,多線程是實(shí)現(xiàn)異步IO的一個(gè)手段而已,它不是必須的,后面會(huì)講到如何通過一個(gè)線程來實(shí)現(xiàn)異步IO。
                      Thread thread = new Thread(() => 
                      {
                          try
                          {
                              int res = Http();
                              action(res);
                          }
                          catch
                          {
                              error();
                          }
                
                      });

                      thread.Start();
                  }

                  public static Task<intHttpAsync()
                  {
                      return Task.Run(() => 
                      {
                          return Http();
                      });
                  }


                  public static void OnMouse_DOWN(string msg)
                  {
                      HttpAsync()
                          .ContinueWith(t => 
                          {
                              if(t.Status == TaskStatus.Faulted)
                              {

                              }else if(t.Status == TaskStatus.RanToCompletion)
                              {
                                  Console.WriteLine(1);
                                  //做一些工作
                              }
                          })
                          .ContinueWith(t => 
                          {
                              if (t.Status == TaskStatus.Faulted)
                              {

                              }
                              else if (t.Status == TaskStatus.RanToCompletion)
                              {
                                  Console.WriteLine(2);
                                  //做一些工作
                              }
                          })
                          .ContinueWith(t => 
                          {
                              if (t.Status == TaskStatus.Faulted)
                              {

                              }
                              else if (t.Status == TaskStatus.RanToCompletion)
                              {
                                  Console.WriteLine(3);
                                  //做一些工作
                              }
                          });
                  }

                  static void Main(string[] args)
                  {
                      while (true)
                      {
                          string msg = GetMessage();
                          if (msg == "quit"return;
                          string m = TranslateMessage(msg);
                          DispatherMessage(m);
                      }
                  }
              }

          在OnMouse_DOWN這個(gè)處理函數(shù)中,我們使用Task的ContinueWith函數(shù)進(jìn)行鏈?zhǔn)讲僮鳎鉀Q了回調(diào)地獄問題,但是總感覺有點(diǎn)那么不爽,我們假想有個(gè)關(guān)鍵字await它能實(shí)現(xiàn)以下作用:首先await必須是Task類型,必須是Task類型的(其實(shí)不是必要條件,后面會(huì)講到)原因是保證必須有ContinueWith這個(gè)函數(shù),如果Task沒有返回值,則把a(bǔ)wait后面的代碼放到Task中的ContinueWith函數(shù)體內(nèi),如果有返回值,則把Await后的結(jié)果轉(zhuǎn)化為訪問Task.Result屬性,文字說的可能不明白,看下示例代碼

          //無返回值轉(zhuǎn)換前
          public async void Example()
          {
              Task t = Task.Run(() =>
              {
                  Thread.Sleep(1000);
              });
              await t;
              //做一些工作
          }
          //無返回值轉(zhuǎn)換后
          public void Example()
          {
              Task t = Task.Run(() =>
              {
                  Thread.Sleep(1000);
              });
              t.ContinueWith(task => 
              {
                  //做一些工作
              });
          }

          //有返回值轉(zhuǎn)換前
          public async void Example()
          {
              Task<int> t = Task.Run<int>(() =>
              {
                  Thread.Sleep(1000);
                  return 1;
              });
              int res = await t;
              //使用res做一些工作
          }
          //有返回值轉(zhuǎn)換后
          public void Example()
          {
              Task<int> t = Task.Run<int>(() =>
              {
                  Thread.Sleep(1000);
                  return 1;
              });
              t.ContinueWith(task => 
              {
                  //使用task.Result做一些工作
              });
          }

          看起來不錯(cuò),但至少有以下問題,如下:

          • 該種轉(zhuǎn)換方法不能很好的轉(zhuǎn)換Try/Catch結(jié)構(gòu)
          • 在循環(huán)結(jié)構(gòu)中使用await不好轉(zhuǎn)換
          • 該實(shí)現(xiàn)與Task類型緊密聯(lián)系

          一二點(diǎn)是我自己認(rèn)為的,但第三點(diǎn)是可以從擴(kuò)展async/await這點(diǎn)被證明的。但無論怎樣,async/await只是對(duì)方法按照一定的規(guī)則進(jìn)行了變換而已,它并沒有什么特別之處,具體來講,就是把Await后面要執(zhí)行的代碼放到一個(gè)類似ContinueWith的函數(shù)中,在C#中,它是以狀態(tài)機(jī)的形式表現(xiàn)的,每個(gè)狀態(tài)都對(duì)應(yīng)一部分代碼,狀態(tài)機(jī)有一個(gè)MoveNext()方法,MoveNext()根據(jù)不同的狀態(tài)執(zhí)行不同的代碼,然后每個(gè)狀態(tài)部分對(duì)應(yīng)的代碼都會(huì)設(shè)置下一個(gè)狀態(tài)字段,然后把自身的MoveNext()方法放到類似ContinueWith()的函數(shù)中去執(zhí)行,整個(gè)狀態(tài)機(jī)由回調(diào)函數(shù)推動(dòng)。我們嘗試手動(dòng)轉(zhuǎn)換以下async/await方法。

          public static Task WorkAsync()
          {
              return Task.Run(() => 
              {
                  Thread.Sleep(1000);
                  Console.WriteLine("Done!");
              });
          }
          public static async void Test()
          {
              Console.WriteLine("步驟1");
              await WorkAsync();
              Console.WriteLine("步驟2");
              await WorkAsync();
              Console.WriteLine("步驟3");
          }

          手動(dòng)寫一個(gè)簡(jiǎn)單的狀態(tài)機(jī)類

           public class TestAsyncStateMachine
              {
                  public int _state = 0;
                  public void Start() => MoveNext();
                  public void MoveNext()
                  {
                      switch(_state)
                      {
                          case 0:
                              {
                                  goto Step0;
                              }
                          case 1:
                              {
                                  goto Step1;
                              }
                          default:
                              {
                                  Console.WriteLine("步驟3");
                                  return;
                              }
                      }

                  Step0:
                      {
                          Console.WriteLine("步驟1");
                          _state = 1;
                          WorkAsync().ContinueWith(t => this.MoveNext());
                          return;
                      }
                  Step1:
                      {
                          _state = -1;
                          Console.WriteLine("步驟2");
                          WorkAsync().ContinueWith(t => this.MoveNext());
                          return;
                      }

                  }
              }

          而Test()方法則變成了這樣

          public static void Test()
          {
              new TestAsyncStateMachine().Start();
          }

          注意Test()方法返回的是void,這意味這調(diào)用方將不能await Test()。如果返回Task,這個(gè)狀態(tài)機(jī)類是不能正確處理的,如果要正確處理,那么狀態(tài)機(jī)在Start()啟動(dòng)后,必須返回一個(gè)Task,而這個(gè)Task在整個(gè)狀態(tài)機(jī)流轉(zhuǎn)完畢后要變成完成狀態(tài),以便調(diào)用方在該Task上調(diào)用的ContinueWith得以繼續(xù)執(zhí)行,而就Task這個(gè)類而言,它是沒有提供這種方法來主動(dòng)控制Task的狀態(tài)的,這個(gè)與JS中的Promise不同,JS里面用Reslove函數(shù)來主動(dòng)控制Promise的狀態(tài),并導(dǎo)致在該P(yáng)romise上面的Then鏈?zhǔn)秸{(diào)用得以繼續(xù)完成,而在C#里面怎么做呢?既然使用了狀態(tài)機(jī)來實(shí)現(xiàn)async/await,那么在轉(zhuǎn)換一個(gè)返回Task的函數(shù)時(shí)肯定會(huì)遇到,怎么處理?后面講。

          首先解決一下與Task類型緊密聯(lián)系這個(gè)問題。

          從狀態(tài)機(jī)中可以看到,主要使用到了Task中的ContinueWith這個(gè)函數(shù),它的語義是在任務(wù)完成后,執(zhí)行回調(diào)函數(shù),通過回調(diào)函數(shù)拿到結(jié)果,這個(gè)編程風(fēng)格也叫做CPS(Continuation-Passing-Style, 續(xù)體傳遞風(fēng)格),那么我們能不能把這個(gè)函數(shù)給抽象出來呢?語言開發(fā)者當(dāng)然想到了,它被抽象成了一個(gè)Awaiter因此編譯器要求await的類型必須要有GetAwaiter方法,什么樣的類型才是Awaiter呢?編譯器規(guī)定主要實(shí)現(xiàn)了如下幾個(gè)方法的類型就是Awaiter:

          • 必須繼承INotifyCompletion接口,并實(shí)現(xiàn)其中的OnCompleted(Action continuation)方法
          • 必須包含IsCompleted屬性
          • 必須包含GetResult()方法

          第一點(diǎn)好理解,第二點(diǎn)的作用是熱路徑優(yōu)化,第三點(diǎn)以后講。我們?cè)俑脑煲幌挛覀兪謩?dòng)寫的狀態(tài)機(jī)。

          public class TestAsyncStateMachine
          {
              public int _state = 0;
              public void Start() => MoveNext();
              public void MoveNext()
              {
                  switch(_state)
                  {
                      case 0:
                          {
                              goto Step0;
                          }
                      case 1:
                          {
                              goto Step1;
                          }
                      default:
                          {
                              Console.WriteLine("步驟3");
                              return;
                          }
                  }

              Step0:
                  {
                      Console.WriteLine("步驟1");
                      _state = 1;
                      TaskAwaiter taskAwaiter;
                      taskAwaiter = WorkAsync().GetAwaiter();
                      if (taskAwaiter.IsCompleted) goto Step1;
                      taskAwaiter.OnCompleted(() => this.MoveNext());
                      return;
                  }
              Step1:
                  {
                      _state = -1;
                      Console.WriteLine("步驟2");
                      TaskAwaiter taskAwaiter;
                      taskAwaiter = WorkAsync().GetAwaiter();
                      if (taskAwaiter.IsCompleted) MoveNext();
                      taskAwaiter.OnCompleted(() => this.MoveNext());
                      return;
                  }

              }
          }

          可以看到去掉了與Task中ContinueWith的耦合關(guān)系,并且如果任務(wù)已經(jīng)完成,則可以直接執(zhí)行下個(gè)任務(wù),避免了無用的開銷。??因此我們可以總結(jié)一下async/await:

          • async/await只是表示這個(gè)方法需要編譯器進(jìn)行特殊處理,并不代表它本身一定是異步的。
          • Task類中的GetAwaiter主要是給編譯器用的。


          //該類型包含GetAwaiter方法,且GetAwaiter()返回的類型包含三個(gè)必要條件
          public class MyAwaiter : INotifyCompletion
          {
              public void OnCompleted(Action continuation)
              {
                  continuation();
              }

              public bool IsCompleted { get; }
              public void GetResult()
              {
              
              }

              public MyAwaiter GetAwaiter() => new MyAwaiter();
          }

          一個(gè)測(cè)試函數(shù),注意必須返回void

          public static async void AwaiterTest()
          {
              await new MyAwaiter();
              Console.WriteLine("Done");
          }

          可以看到這是完全同步進(jìn)行的。

          轉(zhuǎn)自:白煙染黑墨

          鏈接:cnblogs.com/hkfyf/p/14641844.html

          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開發(fā)者峰會(huì)資料PPT
          回復(fù) 【加群】加入dotnet微信交流群

          為什么阿里巴巴禁止使用存儲(chǔ)過程?


          去TM收費(fèi),我要在線 Vip 視頻解析!


          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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在线欧美 | 国内精品国产三级国产99 | 黄A在线免费观看 |