對[yield]的淺究到發現[async][await]


  上篇對[foreach]的淺究到發現[yield]寫完后,覺得對[yield]還沒有理解清楚,想起曾經看過一位大牛的帖子講的很深刻(鏈接在此),回顧了下,在這里寫出自己的理解,與各位分享。

一、通常的異步

  現在我們假設一種平時經常遇到的情況,現有三個方法,其中funcOne和funcTwo比較耗時需要異步執行,而且他們的邏輯是必須在funcOne執行完后才可以執行funcTwo,同理funcTwo執行完后才能執行funcThree。

  按照這樣的設定,通常的做法請看代碼段[1]:

 1      public class Program
 2      {
 3          public delegate void CallBack(string nextName);
 4          public void funcOne(CallBack callback)
 5          {
 6              ThreadPool.QueueUserWorkItem((state1) =>
 7              {
 8                  Console.WriteLine("[One] async Continue!");
 9                  Console.WriteLine("[One] do something!");
10                  callback("Called Two");
11              });
12          }
13          public void funcTwo(CallBack callback)
14          {
15              ThreadPool.QueueUserWorkItem((state2) =>
16                     {
17                         Console.WriteLine("[Two] async Continue!");
18                         Console.WriteLine("[Two] do something!");
19                         callback("Called Three");
20                     });
21          }
22          public void funcThree(CallBack callback)
23          {
24              Console.WriteLine("[Three] do something!");
25              callback("Called ...");
26          }
27          static void Main()
28          {
29              Program p = new Program();
30              p.funcOne((name1) =>
31              {
32                  Console.WriteLine(name1);
33                  p.funcTwo((name2) =>
34                  {
35                      Console.WriteLine(name2);
36                      //執行funcThree
37                      p.funcThree((name3) =>
38                      {
39                          Console.WriteLine(name3);
40                          //當然還有可能繼續嵌套
41                          Console.WriteLine("End!");
42                      });
43                  });
44              });
45              Console.Read();
46          }
47      }
異步的通常實現

  相信看完代碼后我們的感覺是一樣的,好繁瑣,就是不斷的嵌套!那有沒有方法可以避免這樣呢,也就是說用同步的寫法來寫異步程序。

二、改進后的異步

  該[yield]粉墨登場了,先看代碼段[2]:

 1         //三個方法以及委托CallBack的定義不變,此處不再列出。
 2         //新增了靜態的全局變量enumerator,和靜態方法funcList.
 3         public static System.Collections.IEnumerator enumerator = funcList();
 4         public static System.Collections.IEnumerator funcList()
 5         {
 6             Program p = new Program();
 7             p.funcOne((name1) =>
 8             {
 9                 enumerator.MoveNext();
10             });
11             yield return 1;
12             Console.WriteLine("Called Two");
13             p.funcTwo((name2) =>
14             {
15                 enumerator.MoveNext();
16             });
17             yield return 2;
18             Console.WriteLine("Called Three");
19             p.funcThree((name3) =>
20             {
21                 //當然還有可能繼續嵌套
22             });
23             Console.WriteLine("Called ...");
24             Console.WriteLine("End!");
25             yield return 3;
26         }
27 
28       //變化后的Main函數
29       static void Main()
30         {
31             enumerator.MoveNext();
32             Console.Read();
33         }    
改進后的異步

  現在看看,是不是清爽了一些,沒有無止盡的嵌套。代碼中我們只需要定義一個迭代器,在迭代器中調用需要同步執行的方法,用[yield]來分隔,各方法通過在callback里調用迭代器的MoveNext()方法來保持同步。

  通過這樣的實踐,我們可以理解為每當[yield return ..],程序會把迭代器的[上下文環境]暫時保存下來,等到MoveNext()時,再調出來繼續執行到下一個[yield return ..]。

三、是我想太多

  以上純屬瞎扯,在.net 4.5之后,我們有了神器:async/await,下面看看多么簡潔吧。

  代碼段[3]:

 1      public class Program
 2     {
 3         public async Task funcOne()
 4         {
 5             Console.WriteLine("[One] async Continue!");
 6             Console.WriteLine("[One] do something!");
 7             //await ...
 8         }
 9         public async Task funcTwo()
10         {
11             Console.WriteLine("[Two] async Continue!");
12             Console.WriteLine("[Two] do something!");
13             //await ...
14         }
15         public void funcThree()
16         {
17             Console.WriteLine("[Three] do something!");
18         }
19         public static async Task funcList()
20         {
21             Program p = new Program();
22             await p.funcOne();
23             await p.funcTwo();
24             p.funcThree();
25             //無盡的嵌套可以繼續await下去。
26             Console.WriteLine("End!");
27         }
28         static void Main()
29         {
30             funcList();
31             Console.Read();
32         }
33     }
async/await

  我已經感覺到了您的驚嘆之情。

  async修飾符將方法指定為異步方法(注意!:異步不異步,並不是async說了算,如果這個方法中沒有await語句,就算用了async修飾符,它依然是同步執行,因為它就沒有異步基因)。

  被指定為異步方法后,方法的返回值只能為Task、Task<TResult>或者void,返回的Task對象用來表示這個異步方法,你可以對這個Task對象進行控制來操作這個異步方法,詳細的可以參考msdn中給出的Task解釋與示例代碼

  await用於掛起主線程(這里的主線程是相對的),等待這個異步方法執行完成並返回,接着才繼續執行主線程的方法。用了await,主線程才能得到異步方法返回的Task對象,以便於在主線程中對它進行操作。如果一個異步方法調用時沒有用await,那么它就會去異步執行,主線程不會等待,就像我們代碼段[3]中Main方法里對funcList方法的調用那樣。

 

  最后就分析到這兒吧,希望各位兄弟博友能一起討論,共同進步。

  當然,別吝嗇您的[贊]哦 :)

 

 更新:現在大家看到的是改進之后的文章,在此謝謝文章下面幾位高手的討論,讓我學會了不少。抱拳!

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM