程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 對[yield]的淺究到發現[async][await]

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

編輯:C#入門知識

  上篇對[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方法的調用那樣。

 

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

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

 

 

  

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved