欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解C#中的Async和Await用法

 更新時(shí)間:2015年07月13日 10:08:08   投稿:goldensun  
這篇文章主要介紹了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ì)幫助我們從某些地方去取一批訂單。

2015713100246385.png (400×432)

 即使文章從左或從由開始,人們還是習(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)勢(shì);它讓我們使用基于事件的異步模式更為一致。

2015713100321237.png (600×332)

 在你看到一個(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如下:
 

class Order
{
  public string OrderNumber { get; set; }
  public decimal OrderTotal { get; set; }
  public string Reference { get; set; }
}
class OrderHandler
{
  private readonly IEnumerable<Order> _orders;
  public OrderHandler()
  {
    _orders = new[]
        {
          new Order {OrderNumber = "F1", OrderTotal = 100, Reference = "Filip"},
          new Order {OrderNumber = "F1", OrderTotal = 100, Reference = "Filip"}
        };
  }
  public IEnumerable<Order> GetAllOrders()
  {
    return _orders;
  }
}

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

System.Threading.ManualResetEvent(false).WaitOne(2000) in GetAllOrders:
public IEnumerable<Order> GetAllOrders()
{
  System.Threading.ManualResetEvent(false).WaitOne(2000);
  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:

 

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
  <Grid.RowDefinitions>
    <RowDefinition Height="140"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>
 
  <TextBlock x:Name="pageTitle" Margin="120,0,0,0" Text="Order System" Style="{StaticResource PageHeaderTextStyle}" Grid.Column="1" IsHitTestVisible="false"/>
  <StackPanel Grid.Row="1" Margin="120,50,0,0">
    <TextBlock x:Name="Information" />
    <ProgressBar x:Name="OrderLoadingProgress" HorizontalAlignment="Left" Foreground="White" Visibility="Collapsed" IsIndeterminate="True" Width="100">
      <ProgressBar.RenderTransform>
        <CompositeTransform ScaleX="5" ScaleY="5" />
      </ProgressBar.RenderTransform>
    </ProgressBar>
    <ListView x:Name="Orders" DisplayMemberPath="OrderNumber" />
  </StackPanel>
  <AppBar VerticalAlignment="Bottom" Grid.Row="1">
    <Button Content="Load orders" x:Name="LoadOrders" Click="LoadOrders_Click" />
  </AppBar>
</Grid>

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

public MainPage()
{
  this.InitializeComponent();
 
  Information.Text = "No orders have been loaded yet.";
}
private void LoadOrders_Click(object sender, RoutedEventArgs e)
{
  OrderLoadingProgress.Visibility = Visibility.Visible;
  var orderHandler = new OrderHandler();
  var orders = orderHandler.GetAllOrders();
  OrderLoadingProgress.Visibility = Visibility.Collapsed;
}

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

2015713100358213.png (600×371)

看下底部的應(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)該看起來像這樣:
 

public sealed partial class MainPage : Page
{
  private BackgroundWorker _worker = new BackgroundWorker();
  public MainPage()
  {
    InitializeComponent();
 
    _worker.RunWorkerCompleted += WorkerRunWorkerCompleted;
    _worker.DoWork += WorkerDoWork;
  }
 
  void WorkerDoWork(object sender, DoWorkEventArgs e)
  {
    var orderHandler = new OrderHandler();
    var orders = orderHandler.GetAllOrders();
  }
 
  private void LoadOrders_Click(object sender, RoutedEventArgs e)
  {
    OrderLoadingProgress.Visibility = Visibility.Visible;
    _worker.RunWorkerAsync();
  }
 
  void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
    Dispatcher.BeginInvoke(new Action(() =>
    {
      // Update the UI
      OrderLoadingProgress.Visibility = Visibility.Collapsed;
    }));
  }
}

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)記,是由‘a(chǎn)waitable'的標(biāo)記指明的,此后我們請(qǐng)求等待任務(wù)完成。


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

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<Order>的task,就像這樣:
 

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

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

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


當(dāng)我們?cè)谝粋€(gè)task前增加了等待,訂單變量的類型就是task期待返回的類型;在這個(gè)例子中是IEnumerable<Order>。這意味著我們要使這個(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)用:
 

private async void LoadOrders_Click(object sender, RoutedEventArgs e)
{
  OrderLoadingProgress.Visibility = Visibility.Visible;
      
  var orderHandler = new OrderHandler();
 
  var orderTask = Task<IEnumerable<Order>>.Factory.StartNew(() =>
  {
    return orderHandler.GetAllOrders();
  });
 
  var orders = await orderTask;
 
  Orders.Items.Clear();
  foreach (var order in orders)
    Orders.Items.Add(order);
 
  OrderLoadingProgress.Visibility = Visibility.Collapsed;
}

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

2015713100421536.png (600×371)

 新方法帶來的好處事顯而易見的,它使得代碼更線性、更具可讀性。 當(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)用的生命周期。

相關(guān)文章

  • unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲

    unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C#實(shí)現(xiàn)XOR密碼(異或密碼)的示例代碼

    C#實(shí)現(xiàn)XOR密碼(異或密碼)的示例代碼

    XOR密碼(異或密碼)是一種簡單的加密算法,它使用異或(XOR)操作來對(duì)明文和密鑰進(jìn)行加密和解密,本文為大家介紹了C#實(shí)現(xiàn)XOR密碼的相關(guān)知識(shí),希望對(duì)大家有所幫助
    2024-01-01
  • c#圖片添加水印的實(shí)例代碼

    c#圖片添加水印的實(shí)例代碼

    這篇文章介紹了c#圖片添加水印的實(shí)例代碼,有需要的朋友可以參考一下
    2013-07-07
  • 簡單了解C#設(shè)計(jì)模式編程中的橋接模式

    簡單了解C#設(shè)計(jì)模式編程中的橋接模式

    這篇文章主要介紹了C#設(shè)計(jì)模式編程中的橋接模式,橋接模式經(jīng)常應(yīng)用于解耦邏輯層與數(shù)據(jù)操作層,需要的朋友可以參考下
    2016-02-02
  • C#的四個(gè)基本技巧

    C#的四個(gè)基本技巧

    C#的四個(gè)基本技巧...
    2007-03-03
  • C#開發(fā)中經(jīng)常用的加密解密方法示例

    C#開發(fā)中經(jīng)常用的加密解密方法示例

    這篇文章主要給大家介紹了關(guān)于C#開發(fā)中經(jīng)常用的加密解密方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果

    C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果

    這篇文章主要為大家學(xué)習(xí)介紹了如何通過C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下
    2023-07-07
  • C#開發(fā)微信門戶及應(yīng)用(4) 關(guān)注用戶列表及詳細(xì)信息管理

    C#開發(fā)微信門戶及應(yīng)用(4) 關(guān)注用戶列表及詳細(xì)信息管理

    這篇文章主要為大家詳細(xì)介紹了C#開發(fā)微信門戶及應(yīng)用第四篇,關(guān)注用戶列表及詳細(xì)信息管理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • c# Form中的鍵盤響應(yīng)具體實(shí)現(xiàn)思路

    c# Form中的鍵盤響應(yīng)具體實(shí)現(xiàn)思路

    在全屏Form中加上鍵盤ESC的響應(yīng),實(shí)現(xiàn)的效果就是:全屏中press鍵盤上的Escape鍵,程序結(jié)束,具體實(shí)現(xiàn)步驟如下,感興趣的朋友可以參考下哈
    2013-06-06
  • WPF如何自定義TabControl控件樣式示例詳解

    WPF如何自定義TabControl控件樣式示例詳解

    這篇文章主要給大家介紹了關(guān)于WPF如何自定義TabControl控件樣式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04

最新評(píng)論