上篇對[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 //執行funcThree37 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方法的調用那樣。
最后就分析到這兒吧,希望各位兄弟博友能一起討論,共同進步。
當然,別吝嗇您的[贊]哦 :)
更新:現在大家看到的是改進之后的文章,在此謝謝文章下面幾位高手的討論,讓我學會了不少。抱拳!
新聞熱點
疑難解答