上篇對[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 Console.WriteLine("[One] async Continue!");
7 Console.WriteLine("[One] do something!");
8 callback("Called Two");
9 }
10 public void funcTwo(CallBack callback)
11 {
12 Console.WriteLine("[Two] async Continue!");
13 Console.WriteLine("[Two] do something!");
14 callback("Called Three");
15 }
16 public void funcThree(CallBack callback)
17 {
18 Console.WriteLine("[Three] do something!");
19 callback("Called ...");
20 }
21 static void Main()
22 {
23 Program p = new Program();
24 //異步執行funcOne
25 ThreadPool.QueueUserWorkItem((state1)=>{
26 p.funcOne((name1) =>
27 {
28 Console.WriteLine(name1);
29 //異步執行funcTwo
30 ThreadPool.QueueUserWorkItem((state2) =>
31 {
32 p.funcTwo((name2) =>
33 {
34 Console.WriteLine(name2);
35 //執行funcThree
36 p.funcThree((name3) =>
37 {
38 Console.WriteLine(name3);
39 //當然還有可能繼續嵌套
40 Console.WriteLine("End!");
41 });
42 });
43 });
44 });
45 });
46 Console.Read();
47 }
48 }
異步的通常實現
相信看完代碼後我們的感覺是一樣的,好繁瑣,就是不斷的嵌套!那有沒有方法可以避免這樣呢,也就是說用同步的寫法來寫異步程序。
二、改進後的異步
該[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 //異步執行funcOne
8 ThreadPool.QueueUserWorkItem((state1) =>
9 {
10 p.funcOne((name1) =>
11 {
12 enumerator.MoveNext();
13 });
14 });
15 yield return 1;
16 Console.WriteLine("Called Two");
17 //異步執行funcTwo
18 ThreadPool.QueueUserWorkItem((state2) =>
19 {
20 p.funcTwo((name2) =>
21 {
22 enumerator.MoveNext();
23 });
24 });
25 yield return 2;
26 Console.WriteLine("Called Three");
27 //執行funcThree
28 p.funcThree((name3) =>
29 {
30 //當然還有可能繼續嵌套
31 });
32 Console.WriteLine("Called ...");
33 Console.WriteLine("End!");
34 yield return 3;
35 }
36
37 //變化後的Main函數
38 static void Main()
39 {
40 enumerator.MoveNext();
41 Console.Read();
42 }
改進後的異步寫法
現在看看,是不是清爽了一些,沒有無止盡的嵌套。代碼中我們只需要定義一個迭代器,在迭代器中調用需要同步執行的方法,用[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方法的調用那樣。
最後就分析到這兒吧,希望各位兄弟博友能一起討論,共同進步。
當然,別吝啬您的[贊]哦 :)