Visual Studio Async CTP的實現原理淺析 – 如何不使用async和await關鍵字來實現Async

來源:互聯網
上載者:User

Async CTP為我們在單線程實現非同步作業開闢了一條大道,尤其對於SL中的WCF來說讓我們從繁瑣的事件處理中解脫出來,本來我想寫一個SL中使用Socket實現的RPC架構(正在我項目中使用)的系列筆記,不過有朋友提到了應該用Async CTP來規避WCF的非同步方法呼叫帶來的繁瑣,事實上Async CTP在我項目中還不夠靈活(應該是WCF對我的應用程式來說不夠靈活),不過還是先放下那個系列,先看看Async CTP可以做些什麼。

本文不會介紹Async CTP的使用方法,只分析在不改動SL CLR的基礎上編譯器如何根據async和await兩個關鍵字對我們的代碼做出正確的改動以達到單線程非同步作業。

關於單線程非同步可以參考 Programming user interfaces using F# workflows

要理解Async CTP強力推薦此文 Asynchronous Programming in C# using Iterators

先來看看AsyncCtpLibrary_Silverlight.dll中的幾個重要的類,在System.Runtime.CompilerServices命名空間中找出這幾個類AsyncMehodBuilder, AsyncMethodBuilder<TResult>, VoidAsyncMethodBuilder, TaskAWaiter, TaskAwaiter<TResult>,以及System.Threading.Tasks下面的Task, Task<TResult>看到名字基本可以知道是幹啥的了,簡單介紹一下:

AsyncMethodBuilder: 假如async被施加在一個Task對象之前,編譯器使用這個類。

AsyncMethodBuilder<TResult>: 假如async被施加在Task<TResult>之前, 編譯器使用這個類。

VoidAsyncMethodBuilder: async被施加在一個方法聲明之前,編譯器使用這個類。(因此只有傳回值是void的方法能加async關鍵字)

TaskAwaiter: 對應在Task對象之前的await關鍵字。

TaskAwaiter<TResult>:對應在Task<TResult>對象之前的await關鍵字。

Task、Task<TResult>:對應兩個await。

為了實現async,await編譯器將每個被async關鍵字標記的方法編譯為一個方法所在類的一個內嵌類,所有在方法體內出現的變數會被聲明為這個類的field,如果是一個執行個體方法,那麼this所代表的對象也被聲明為一個field。這個類有兩個核心成員:一個int來儲存代碼執行到那一步,暫且叫它step,一個方法來執行真正的動作,暫且叫做NextStep,整個邏輯看起來應該是這樣的:

   1:  public void NextStep()
   2:  {
   3:      switch (step)
   4:      {
   5:          case 1:
   6:              ...
   7:              step++;
   8:              break;
   9:          case 2:
  10:              ...
  11:              step++;
  12:              break;
  13:          case 3:
  14:              ....
  15:              step++;
  16:              break;
  17:          .
  18:          .
  19:          .
  20:      }
  21:  }

是不是覺得和yield return很像?

而在async標記的方法大體是如此,假設方法被編譯為一個命為AsyncMethodClass的類:

   1:  AsyncMethodClass a = new AsyncMethodClass();
   2:  a.xxx = xxx;
   3:  a.yyy = yyy;
   4:  .
   5:  .
   6:  .
   7:  a.NextStep();

那麼編譯根據什麼來決定一個方法分成幾個塊呢?實際上,編譯器根據一個async方法中出現的await關鍵字來進行分布,假如有一個await,那麼應該有2個Step,假如有2個await,那麼應該有3個Step。每一個Step,應該是以上一個Step中的await的EndWait開始(第一個Step除外),並以下一await的BeginWait結束(最後一個Step除外)。看到這裡就理解了為什麼async只能被標記在無傳回值的函數上,因為NextStep函數必須要是一個無傳回值的Action類型,傳遞給await的BeginWait方法,因此在上述代碼中,在最後的第7行沒有辦法使用理論上的:

   1:  return a.NextStep();

弄明白了編譯器乾的活之後,最後來看一個執行個體,來自於Async CTP Samples的片段:

   1:          public async void AsyncIntroSerial()
   2:          {
   3:              var client = new WebClient();
   4:   
   5:              WriteLinePageTitle(await client.DownloadStringTaskAsync(new Uri("http://www.weather.gov")));
   6:              WriteLinePageTitle(await client.DownloadStringTaskAsync(new Uri("http://www.weather.gov/climate/")));
   7:              WriteLinePageTitle(await client.DownloadStringTaskAsync(new Uri("http://www.weather.gov/rss/")));
   8:          }

經過上面的理論,不使用async和await關鍵字,改寫如下:

   1:          TaskAwaiter<string> wait1;
   2:          TaskAwaiter<string> wait2;
   3:          TaskAwaiter<string> wait3;
   4:          int step = 0;
   5:          WebClient wc = new WebClient();
   6:          private void AsyncMethodWithoutKeywords()
   7:          {
   8:              switch (step)
   9:              {
  10:                  case 0:
  11:                      Task<string> t1 = wc.DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
  12:                      step++;
  13:                      wait1 = t1.GetAwaiter<string>();
  14:                      wait1.BeginAwait(new Action(AsyncMethodWithoutKeywords));
  15:                      break;
  16:                  case 1:
  17:                      WriteLinePageTitle(wait1.EndAwait());
  18:                      Task<string> t2 = wc.DownloadStringTaskAsync(new Uri("http://www.weather.gov/climate/"));
  19:                      step++;
  20:                      wait2 = t2.GetAwaiter<string>();
  21:                      wait2.BeginAwait(new Action(AsyncMethodWithoutKeywords));
  22:                      break;
  23:                  case 2:
  24:                      WriteLinePageTitle(wait2.EndAwait());
  25:                      Task<string> t3 = wc.DownloadStringTaskAsync(new Uri("http://www.weather.gov/rss/"));
  26:                      step++;
  27:                      wait3 = t3.GetAwaiter<string>();
  28:                      wait3.BeginAwait(new Action(AsyncMethodWithoutKeywords));
  29:                      break;
  30:                  case 4:
  31:                      WriteLinePageTitle(wait3.EndAwait());
  32:                      break;
  33:              }
  34:          }

可以發現,執行結果和使用async和await一樣。

Async CTP的核心其實是Task的設計,CTP庫為WebClient和WCF實現了Async到Task的工作,對於項目中出現自訂的一些方法則需要自己去定義Task,不過通過自己的方法定義Task,並且使用本文所描述的方法進行封裝(尤其是在原先使用Emit或者使用dynamic關鍵字來做AOP的場合),那麼可以拋開aysnc和await關鍵字,阻止編譯器對代碼的改動,這樣的好處是斷點調試可以正常進行。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.