亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

詳解C#中的Async和Await用法

2019-10-29 21:41:14
字體:
來源:轉載
供稿:網友

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

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

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

線性代碼vs非線性代碼

大部分的軟件工程師都習慣用一種線性的方式去編程,至少這是他們開始職業生涯時就被這樣教導。當一個程序使用線性方式去編寫,這意味著它的源代碼讀起來有的像Figure 1展示的。這就是假設有一個適當的訂單系統會幫助我們從某些地方去取一批訂單。

詳解C#中的Async和Await用法

即使文章從左或從由開始,人們還是習慣于從上到下地閱讀。如果我們有某些東西影響到了這個內容的順序,我們將會感到困惑同時在這上面比實際需要的事情上花費更多努力?;谑录某绦蛲ǔ碛羞@些非線性的結構。

基于事件系統的流程是這樣的,它在某處發起一個調用同時期待結果通過一個觸發的時間傳遞,Figure 2 展示的很形象的表達了這點。初看這兩個序列似乎不是很大區別,但如果我們假設GetAllOrders返回空,我們檢索訂單列表就沒那么直接了當了。

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

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

詳解C#中的Async和Await用法

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

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

介紹一個新的模式

讓 我們假設我們在自己的系統上工作,系統使用上面提到過的OrderHandler以及實際實現是使用一個線性方法。為了模擬一小部分的真是訂單系統,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; 

因為我們在例子里不使用真是的數據源,我們需要讓它有那么一點更為有趣的。由于這是關于異步編程的,我們想要在一個異步的方式中請求一些東西。為了模擬這個,我們簡單的加入:

 

 
  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的原因是這段代碼將會加入到Windows8商店應用程序。這里的目的是在這里我們將會為我們的加載訂單列表的Windows8商店應用程序放置一個可以按的按鈕。然后,我們可以比較下用戶體驗和在之前加入的異步代碼。

如果你已經創建了一個空的Windows商店應用程序項目,你可以加入如下的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; 

這會帶給我們一個挺好看的應用程序,當我們在Visual Studio 2012的模擬器上運行的時候看起來就像這樣:

詳解C#中的Async和Await用法

看下底部的應用程序工具欄, 通過按這個在右手邊的菜單的圖標進入基本的觸摸模式,然后從下往上刷。

現在當你按下加載訂單按鈕的時候,你會注意到你看不到進度條同時按鈕保持在被按下狀態2秒。這是由于我們把應用程序鎖定了。

以前我們可以通過在一個BackgroundWorker里封裝代碼來解決問題。當完成的時候,它會在我們為改變UI而已調用的委托中出發一個事件。這是一種非線性的方法,但往往會把代碼的可讀性搞得糟糕。在一個非WinRT的訂單應用程序,使用BackgroundWorker應該看起來像這樣:

 

 
  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由于基于事件的異步性而被認識,這種模式叫做基于事件異步模式(EAP)。這往往會使代碼比以前更亂,同時,由于它使用非線性方式編寫,我們的腦袋要花一段事件才能對它有一定的概念。

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

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

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

 

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

同時使用async和void時需要很小心,標記一個異步的方法返回值為void的唯一原因,就是因為事件處理代碼。當方法不是事件處理者,且返回類型為空時,絕不要標記其為異步的!異步與等待總是同時使用的,如果一個方法標記為異步的但其內部卻沒有什么可等待的,它將只會以同步方式執行。

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

 

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

上面就是我們要等待的部分,我們來看看開始我們有的并對比一下現在我們有的:

 

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

當我們在一個task前增加了等待,訂單變量的類型就是task期待返回的類型;在這個例子中是IEnumerable。這意味著我們要使這個方法異步,需要唯一做的就是標記它是異步的,并且將對執行時間長的方法的調用封裝于一個task之內。

內部發生的事情就是我們將用一個狀態機保存task執行結束的印記。等待代碼段的所有代碼將被放入一個繼續點代碼段。如果你對TPL和task的繼續點熟悉,這就與之類似,除了我們到達繼續點便回到了調用線程之外!這是一個重要的區別,因為那意味著我們可以使我們的方法像這樣,而不需要任何分派器的調用:

 

 
  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時用到的分派器?,F在我們可以執行這個應用并且裝載訂單而不鎖定UI,并且然后會很漂亮的獲得許多訂單列表的顯示。

詳解C#中的Async和Await用法

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

結論

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

當然,這并非萬能的??傆羞@個方法也會導致混亂的情形。但只要在適當的地方使用它,將有益于應用的生命周期。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品亚洲片夜色在线| 日韩欧美亚洲范冰冰与中字| 久久久免费高清电视剧观看| 日韩综合中文字幕| 久久亚洲精品一区二区| 日韩欧美亚洲国产一区| 亚洲第一中文字幕| 日韩精品小视频| 国产精品久久久久免费a∨大胸| 日韩成人高清在线| 日韩av在线资源| 亚洲最大的av网站| 91精品视频大全| 国产日韩中文在线| 久久av在线看| 九九九久久国产免费| 中文字幕日韩欧美在线视频| 亚洲人成在线观看| 国产98色在线| 少妇高潮久久77777| 色综合色综合久久综合频道88| 国产日韩专区在线| 国产mv久久久| 成人免费视频xnxx.com| 日本在线精品视频| 精品久久久久久中文字幕| 全亚洲最色的网站在线观看| 亚洲最大的av网站| 亚洲精品久久久久久久久久久久| 久久久精品一区二区| 精品国产福利在线| 日韩亚洲国产中文字幕| 精品视频偷偷看在线观看| 亚洲男人第一网站| 亚洲欧洲激情在线| 一本一本久久a久久精品牛牛影视| 亚洲欧美日韩一区二区在线| 91精品国产自产91精品| 7777免费精品视频| 日韩电影免费观看中文字幕| 亚洲福利视频二区| 国产精品99一区| 欧美日韩久久久久| 日韩美女在线观看一区| 欧美性猛交xxx| 成人在线免费观看视视频| 亚洲天堂开心观看| 亚洲欧美自拍一区| 国产成人av网| 国产亚洲激情在线| 亚洲国产欧美一区二区丝袜黑人| 亚洲国产成人久久综合| 性色av一区二区咪爱| 久久久免费观看视频| 91极品女神在线| 92裸体在线视频网站| 91sa在线看| 中文字幕日韩欧美在线视频| 91国产在线精品| 欧美又大又硬又粗bbbbb| 欧美成人激情在线| 亚洲精品国产suv| 久久精品美女视频网站| 2019中文字幕全在线观看| 性亚洲最疯狂xxxx高清| 欧美成人免费视频| 国产99久久精品一区二区 夜夜躁日日躁| 人人澡人人澡人人看欧美| 日韩av一区二区在线观看| 亚洲福利视频免费观看| 国产美女被下药99| 高清欧美性猛交xxxx黑人猛交| 欧美精品成人91久久久久久久| 中文字幕v亚洲ⅴv天堂| 久久噜噜噜精品国产亚洲综合| 国产成人精品综合久久久| 日韩在线观看免费高清| 亚洲欧美综合另类中字| 国产精品视频久久| 伊人伊人伊人久久| 日韩av电影在线播放| 国产精品久久国产精品99gif| 欧美洲成人男女午夜视频| 国产精品天天狠天天看| 久久久久久国产| 亚洲精品网址在线观看| 亚洲人成网站999久久久综合| 色偷偷88888欧美精品久久久| 欧美精品午夜视频| 欧美国产日韩一区二区| 狠狠躁夜夜躁人人爽天天天天97| 中文字幕欧美日韩va免费视频| 久久韩剧网电视剧| 成人欧美在线观看| 日韩电影中文字幕| 91av视频导航| 欧美黑人巨大xxx极品| 日韩人在线观看| 91久久久国产精品| 国产精品一区久久| 日韩视频―中文字幕| 久精品免费视频| 久久久人成影片一区二区三区观看| 日韩有码在线视频| 亚洲女人天堂成人av在线| 欧美一级免费视频| 日本人成精品视频在线| 久久精品影视伊人网| 久久99国产综合精品女同| 欧美日韩国产精品一区二区不卡中文| 日韩av手机在线观看| 欧美午夜影院在线视频| 国产精品白嫩初高中害羞小美女| 亚洲激情电影中文字幕| 欧美成人中文字幕| 日韩免费视频在线观看| 在线观看成人黄色| 伊人久久精品视频| 亚洲人成在线观看网站高清| 亚洲第一网中文字幕| 色系列之999| 国内精品免费午夜毛片| 亚洲第五色综合网| 97久久精品国产| 91在线观看免费高清完整版在线观看| 日韩精品在线免费观看| 日韩欧美高清在线视频| 日韩精品一区二区三区第95| 久久91亚洲精品中文字幕奶水| 欧美伊久线香蕉线新在线| 久久久久久av| 亚洲精品国产电影| 国产成人福利夜色影视| 欧美性资源免费| 日韩精品www| 在线日韩日本国产亚洲| 国产精品吹潮在线观看| 日本韩国在线不卡| 欧美专区第一页| 亚洲成人黄色网址| 日韩欧美在线观看视频| 北条麻妃一区二区三区中文字幕| 日韩电影大全免费观看2023年上| 色悠久久久久综合先锋影音下载| 久久久噜噜噜久噜久久| 国产精品视频在线观看| 欧美高跟鞋交xxxxxhd| 久久国产天堂福利天堂| 日韩黄色av网站| 亚洲欧洲免费视频| 亚洲国模精品私拍| 国产美女直播视频一区| 欧美在线视频在线播放完整版免费观看| 国产精品美女999| 国产美女搞久久| 日韩欧美在线网址| 国产精品高潮呻吟视频| 欧美成人sm免费视频| 欧美性xxxxhd| 欧美大片va欧美在线播放| 欧美在线激情视频| 91chinesevideo永久地址| 国产在线观看不卡| 精品美女久久久久久免费|