a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 編程 > C# > 正文

詳解C#中的Async和Await用法

2019-10-29 21:41:14
字體:
供稿:網(wǎng)友

這篇文章主要介紹了C#中的Async和Await用法,包括在C#5.0下一些新特性的影響,需要的朋友可以參考下

這篇文章由Filip Ekberg為DNC雜志編寫。

自跟隨著.NET 4.5 及Visual Studio 2012的C# 5.0起,我們能夠使用涉及到async和await關(guān)鍵字的新的異步模式。有很多不同觀點(diǎn)認(rèn)為,比起以前我們看到的,它的可讀性和可用性是否更為突出。我們將通過一個(gè)例子來看下它跟現(xiàn)在的怎么不同。

線性代碼vs非線性代碼

大部分的軟件工程師都習(xí)慣用一種線性的方式去編程,至少這是他們開始職業(yè)生涯時(shí)就被這樣教導(dǎo)。當(dāng)一個(gè)程序使用線性方式去編寫,這意味著它的源代碼讀起來有的像Figure 1展示的。這就是假設(shè)有一個(gè)適當(dāng)?shù)挠唵蜗到y(tǒng)會(huì)幫助我們從某些地方去取一批訂單。

詳解C#中的Async和Await用法

即使文章從左或從由開始,人們還是習(xí)慣于從上到下地閱讀。如果我們有某些東西影響到了這個(gè)內(nèi)容的順序,我們將會(huì)感到困惑同時(shí)在這上面比實(shí)際需要的事情上花費(fèi)更多努力。基于事件的程序通常擁有這些非線性的結(jié)構(gòu)。

基于事件系統(tǒng)的流程是這樣的,它在某處發(fā)起一個(gè)調(diào)用同時(shí)期待結(jié)果通過一個(gè)觸發(fā)的時(shí)間傳遞,F(xiàn)igure 2 展示的很形象的表達(dá)了這點(diǎn)。初看這兩個(gè)序列似乎不是很大區(qū)別,但如果我們假設(shè)GetAllOrders返回空,我們檢索訂單列表就沒那么直接了當(dāng)了。

不看實(shí)際的代碼,我們認(rèn)為線性方法處理起來更加舒服,同時(shí)它更少的有出錯(cuò)的傾向。在這種情況下,錯(cuò)誤可能不是實(shí)際的運(yùn)行時(shí)錯(cuò)誤或者編譯錯(cuò)誤,但是在使用上的錯(cuò)誤;由于缺乏明朗。

基于事件的方法有一個(gè)很大的優(yōu)勢;它讓我們使用基于事件的異步模式更為一致。

詳解C#中的Async和Await用法

在你看到一個(gè)方法的時(shí)候,你會(huì)想去弄明白這方法的目的。這意味著如果你有一個(gè)叫ReloadOrdersAndRefreshUI的方法,你想去弄明白這些訂單從哪里載入,怎樣把它加到UI,當(dāng)這方法結(jié)束的時(shí)候會(huì)發(fā)生什么。在基于事件的方法里,這很難如愿以償。

另外得益于這的是,只要在我們出發(fā)LoadOrdersCompleted事件時(shí),我們能夠在GetAllOrders里寫異步代碼,返回到調(diào)用線程去。

介紹一個(gè)新的模式

讓 我們假設(shè)我們?cè)谧约旱南到y(tǒng)上工作,系統(tǒng)使用上面提到過的OrderHandler以及實(shí)際實(shí)現(xiàn)是使用一個(gè)線性方法。為了模擬一小部分的真是訂單系統(tǒng),OrderHandler和Order如下:

 

 
  1. class Order 
  2. public string OrderNumber { get; set; } 
  3. public decimal OrderTotal { get; set; } 
  4. public string Reference { get; set; } 
  5. class OrderHandler 
  6. private readonly IEnumerable<Order> _orders; 
  7. public OrderHandler() 
  8. _orders = new[] 
  9. new Order {OrderNumber = "F1", OrderTotal = 100, Reference = "Filip"}, 
  10. new Order {OrderNumber = "F1", OrderTotal = 100, Reference = "Filip"
  11. }; 
  12. public IEnumerable<Order> GetAllOrders() 
  13. return _orders; 

因?yàn)槲覀冊(cè)诶永锊皇褂谜媸堑臄?shù)據(jù)源,我們需要讓它有那么一點(diǎn)更為有趣的。由于這是關(guān)于異步編程的,我們想要在一個(gè)異步的方式中請(qǐng)求一些東西。為了模擬這個(gè),我們簡單的加入:

 

 
  1. System.Threading.ManualResetEvent(false).WaitOne(2000) in GetAllOrders: 
  2. public IEnumerable<Order> GetAllOrders() 
  3. System.Threading.ManualResetEvent(false).WaitOne(2000); 
  4. return _orders; 

這里我們不用Thread.Sleep的原因是這段代碼將會(huì)加入到Windows8商店應(yīng)用程序。這里的目的是在這里我們將會(huì)為我們的加載訂單列表的Windows8商店應(yīng)用程序放置一個(gè)可以按的按鈕。然后,我們可以比較下用戶體驗(yàn)和在之前加入的異步代碼。

如果你已經(jīng)創(chuàng)建了一個(gè)空的Windows商店應(yīng)用程序項(xiàng)目,你可以加入如下的XAML到你的MainPage.xml:

 

 
  1. <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
  2. <Grid.RowDefinitions> 
  3. <RowDefinition Height="140"/> 
  4. <RowDefinition Height="*"/> 
  5. </Grid.RowDefinitions> 
  6.  
  7. <TextBlock x:Name="pageTitle" Margin="120,0,0,0" Text="Order System" Style="{StaticResource PageHeaderTextStyle}" Grid.Column="1" IsHitTestVisible="false"/> 
  8. <StackPanel Grid.Row="1" Margin="120,50,0,0"
  9. <TextBlock x:Name="Information" /> 
  10. <ProgressBar x:Name="OrderLoadingProgress" HorizontalAlignment="Left" Foreground="White" Visibility="Collapsed" IsIndeterminate="True" Width="100"
  11. <ProgressBar.RenderTransform> 
  12. <CompositeTransform ScaleX="5" ScaleY="5" /> 
  13. </ProgressBar.RenderTransform> 
  14. </ProgressBar> 
  15. <ListView x:Name="Orders" DisplayMemberPath="OrderNumber" /> 
  16. </StackPanel> 
  17. <AppBar VerticalAlignment="Bottom" Grid.Row="1"
  18. <Button Content="Load orders" x:Name="LoadOrders" Click="LoadOrders_Click" /> 
  19. </AppBar> 
  20. </Grid> 

在我們的程序能跑之前,我們還需要在代碼文件里加入一些東西:

 

 
  1. public MainPage() 
  2. this.InitializeComponent(); 
  3.  
  4. Information.Text = "No orders have been loaded yet."
  5. private void LoadOrders_Click(object sender, RoutedEventArgs e) 
  6. OrderLoadingProgress.Visibility = Visibility.Visible; 
  7. var orderHandler = new OrderHandler(); 
  8. var orders = orderHandler.GetAllOrders(); 
  9. OrderLoadingProgress.Visibility = Visibility.Collapsed; 

這會(huì)帶給我們一個(gè)挺好看的應(yīng)用程序,當(dāng)我們?cè)赩isual Studio 2012的模擬器上運(yùn)行的時(shí)候看起來就像這樣:

詳解C#中的Async和Await用法

看下底部的應(yīng)用程序工具欄, 通過按這個(gè)在右手邊的菜單的圖標(biāo)進(jìn)入基本的觸摸模式,然后從下往上刷。

現(xiàn)在當(dāng)你按下加載訂單按鈕的時(shí)候,你會(huì)注意到你看不到進(jìn)度條同時(shí)按鈕保持在被按下狀態(tài)2秒。這是由于我們把應(yīng)用程序鎖定了。

以前我們可以通過在一個(gè)BackgroundWorker里封裝代碼來解決問題。當(dāng)完成的時(shí)候,它會(huì)在我們?yōu)楦淖僓I而已調(diào)用的委托中出發(fā)一個(gè)事件。這是一種非線性的方法,但往往會(huì)把代碼的可讀性搞得糟糕。在一個(gè)非WinRT的訂單應(yīng)用程序,使用BackgroundWorker應(yīng)該看起來像這樣:

 

 
  1. public sealed partial class MainPage : Page 
  2. private BackgroundWorker _worker = new BackgroundWorker(); 
  3. public MainPage() 
  4. InitializeComponent(); 
  5.  
  6. _worker.RunWorkerCompleted += WorkerRunWorkerCompleted; 
  7. _worker.DoWork += WorkerDoWork; 
  8.  
  9. void WorkerDoWork(object sender, DoWorkEventArgs e) 
  10. var orderHandler = new OrderHandler(); 
  11. var orders = orderHandler.GetAllOrders(); 
  12.  
  13. private void LoadOrders_Click(object sender, RoutedEventArgs e) 
  14. OrderLoadingProgress.Visibility = Visibility.Visible; 
  15. _worker.RunWorkerAsync(); 
  16.  
  17. void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
  18. Dispatcher.BeginInvoke(new Action(() => 
  19. // Update the UI 
  20. OrderLoadingProgress.Visibility = Visibility.Collapsed; 
  21. })); 

BackgroundWorker由于基于事件的異步性而被認(rèn)識(shí),這種模式叫做基于事件異步模式(EAP)。這往往會(huì)使代碼比以前更亂,同時(shí),由于它使用非線性方式編寫,我們的腦袋要花一段事件才能對(duì)它有一定的概念。

但在WinRT中沒有BackgroundWorker,所以我們必須適應(yīng)新的線性方法,這也是一個(gè)好的事情!

我們對(duì)此的解決方法是適應(yīng).NET4.5引入的新的模式,async 與 await。當(dāng)我們使用async 和 await,就必須同時(shí)使用任務(wù)并行庫(TPL)。原則是每當(dāng)一個(gè)方法需要異步執(zhí)行,我們就給它這個(gè)標(biāo)記。這意味著該方法將帶著一些我們等待的東西返回,一個(gè)繼續(xù)點(diǎn)。繼續(xù)點(diǎn)段所在位置的標(biāo)記,是由‘awaitable'的標(biāo)記指明的,此后我們請(qǐng)求等待任務(wù)完成。

基于原始代碼,沒有BackgroundWorker的話我們只能對(duì)click處理代碼做一些小的改變,以便它能應(yīng)用于異步的方式。首先我們需要標(biāo)記該方法為異步的,這簡單到只需將關(guān)鍵字加到方法簽名:

 

 
  1. private async void LoadOrders_Click(object sender, RoutedEventArgs e) 

同時(shí)使用async和void時(shí)需要很小心,標(biāo)記一個(gè)異步的方法返回值為void的唯一原因,就是因?yàn)槭录幚泶a。當(dāng)方法不是事件處理者,且返回類型為空時(shí),絕不要標(biāo)記其為異步的!異步與等待總是同時(shí)使用的,如果一個(gè)方法標(biāo)記為異步的但其內(nèi)部卻沒有什么可等待的,它將只會(huì)以同步方式執(zhí)行。

因此下一個(gè)我們要做的事情事實(shí)上就是保證有一些我們能等待的事情,在我們的例子中就是調(diào)用GetAllOrders。由于這是最耗費(fèi)時(shí)間的部分,我們希望它可以在一個(gè)獨(dú)立的task中執(zhí)行。我們只需將這個(gè)方法打包于一個(gè)期待返回IEnumerable的task,就像這樣:

 

 
  1. Task<IEnumerable<Order>>.Factory.StartNew(() => { return orderHandler.GetAllOrders(); }); 

上面就是我們要等待的部分,我們來看看開始我們有的并對(duì)比一下現(xiàn)在我們有的:

 

 
  1. // Before 
  2. var orders = orderHandler.GetAllOrders(); 
  3.  
  4. // After 
  5. var orders = await Task<IEnumerable<Order>>.Factory.StartNew(() => { return orderHandler.GetAllOrders(); }); 

當(dāng)我們?cè)谝粋€(gè)task前增加了等待,訂單變量的類型就是task期待返回的類型;在這個(gè)例子中是IEnumerable。這意味著我們要使這個(gè)方法異步,需要唯一做的就是標(biāo)記它是異步的,并且將對(duì)執(zhí)行時(shí)間長的方法的調(diào)用封裝于一個(gè)task之內(nèi)。

內(nèi)部發(fā)生的事情就是我們將用一個(gè)狀態(tài)機(jī)保存task執(zhí)行結(jié)束的印記。等待代碼段的所有代碼將被放入一個(gè)繼續(xù)點(diǎn)代碼段。如果你對(duì)TPL和task的繼續(xù)點(diǎn)熟悉,這就與之類似,除了我們到達(dá)繼續(xù)點(diǎn)便回到了調(diào)用線程之外!這是一個(gè)重要的區(qū)別,因?yàn)槟且馕吨覀兛梢允刮覀兊姆椒ㄏ襁@樣,而不需要任何分派器的調(diào)用:

 

 
  1. private async void LoadOrders_Click(object sender, RoutedEventArgs e) 
  2. OrderLoadingProgress.Visibility = Visibility.Visible; 
  3.  
  4. var orderHandler = new OrderHandler(); 
  5.  
  6. var orderTask = Task<IEnumerable<Order>>.Factory.StartNew(() => 
  7. return orderHandler.GetAllOrders(); 
  8. }); 
  9.  
  10. var orders = await orderTask; 
  11.  
  12. Orders.Items.Clear(); 
  13. foreach (var order in orders) 
  14. Orders.Items.Add(order); 
  15.  
  16. OrderLoadingProgress.Visibility = Visibility.Collapsed; 

正如你看到的,我們只需在等待代碼段之后改變UI上的東西,而不需要使用我們前面在用EAP或TPL時(shí)用到的分派器。現(xiàn)在我們可以執(zhí)行這個(gè)應(yīng)用并且裝載訂單而不鎖定UI,并且然后會(huì)很漂亮的獲得許多訂單列表的顯示。

詳解C#中的Async和Await用法

新方法帶來的好處事顯而易見的,它使得代碼更線性、更具可讀性。 當(dāng)然,即使是最好的模式,也能寫出難看的代碼。 異步和待機(jī)確實(shí)能夠使代碼更可讀、更易于維護(hù)。

結(jié)論

Async & Await 使得創(chuàng)建一個(gè)具有可讀性與可維護(hù)性的異步解決方案變得很容易。在本文發(fā)布前,我們不得不求助于可能引起困惑的基于事件的方法。由于我們已處于幾乎所有電腦,甚至手機(jī)都有至少兩個(gè)內(nèi)核的時(shí)代,我們將會(huì)看到更多的并行的異步的代碼。因?yàn)檫@些使得async & await 很容易,所以在開發(fā)階段引入這個(gè)問題已沒有必要。我們能避免由于沒有調(diào)度程序或調(diào)度功能而采用任務(wù)或基于事件的異步性所引起的跨線程的問題。隨著這個(gè)新的模式,我們可以不再陷入聚焦于創(chuàng)建可響應(yīng)可維護(hù)的解決方案的思考。

當(dāng)然,這并非萬能的。總有這個(gè)方法也會(huì)導(dǎo)致混亂的情形。但只要在適當(dāng)?shù)牡胤绞褂盟瑢⒂幸嬗趹?yīng)用的生命周期。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 欧美成人午夜精品久久久 | www.久久久 | 男男高h在线观看 | 91网站在线看 | 日韩综合网 | 国产一级视频 | 欧美一区三区三区高中清蜜桃 | 成人欧美一区二区三区黑人孕妇 | 日韩电影专区 | 男人都懂的www网站免费观看 | 国产区视频在线观看 | 久久av网址 | 黄色三级视频 | 国产精品美女视频一区二区三区 | 玖玖国产精品视频 | 中文字幕一区二区三区日韩精品 | 国产精品成人免费 | 免费在线一区二区 | 91亚洲国产成人久久精品网站 | 亚洲一区二区三区视频 | 久久精品免费观看 | 国产精品久久久久久久久久99 | 久久伊人影院 | 黄色片网址 | 国精日本亚洲欧州国产中文久久 | 国产精品久久久久久久久久久久冷 | 亚洲区在线 | 亚洲国产婷婷香蕉久久久久久 | 欧美日韩无 | 老司机福利在线视频 | 日日精品 | av大片在线免费观看 | 欧美1级| 亚洲免费网| 91精品国产综合久久婷婷香蕉 | 欧美激情网站 | 国产精品视频在线观看 | 欧美黄视频| 日本精品一区 | 欧美一区视频 | 久久亚洲综合 |