非同步程式設計Async與Await

在微軟次世代的Merto UI,要達成良好的UX體驗,非同步的程式設計是少不了的,透過非同步的程式設計更可以妥善的利用CPU的多執行緒功能,平行處理大量的運算,來達成更好的響應率。有關於Async與Await的基本功能,在董大偉先生的這一篇文章,有詳細的說明。

在這邊舉一個更進階的例子,來展示一下Async與Await的寫法,運行環境當然是ASP.NET with .NET Framework 4.5啦!以下僅進行純程式碼說明:

//建立產品物件
public class product
{
  public string name { get; set; }
  public int price { get; set; }
}
//建立產品主控物件
public class productHandler
{
  private System.Collections.Generic.IEnumerable<product> _oProducts;
  public productHandler()
  {
    _oProducts = new[]
    {
      new product{name="AAA", price=100},
      new product{name="BBB", price=200}
    };
  }
  public System.Collections.Generic.IEnumerable<product> getAllProducts()
  {
    //設定一個5秒睡眠,來拖長回應時間
    System.Threading.Thread.Sleep(5000);
    return _oProducts;
  }
}

private async void ClickMe(object sender, EventArgs e)
{
  showMe.Text = "開始" + "換行";
  
  //要達成真實的同步感,一定要丟到背景進行平行處理
  productHandler oTemp1 = new productHandler();
  var oTask1 = System.Threading.Tasks.Task<IEnumerable<product>>.Factory.StartNew(() =>
  {
    return oTemp1.getAllProducts();
  });
  productHandler oTemp2 = new productHandler();
  var oTask2 = System.Threading.Tasks.Task<IEnumerable<product>>.Factory.StartNew(() =>
  {
    return oTemp2.getAllProducts();
  });
  
  //程式遇到await後,會真的等它運行完成回傳後,才往下做,因此await是分歧的終點
  var oResult1 = await oTask1;
  var oResult2 = await oTask2;
  
  foreach (var product in oResult1)
  {
    showMe.Text += product.name + "; " + product.price + "換行";
  }
  foreach (var product in oResult2)
  {
    showMe.Text += product.name + "; " + product.price + "換行";
  }
  
  showMe.Text += "結束";
}

上面的程式碼展示了使用兩次的object.getAllProducts(),但是總耗費時間還是接近五秒鐘,理論上是要10秒才對。所有的重點在於丟到背景運行,這是很多在介紹Async與Await使用方式的網站,都沒有跟你講的東西。更明白的說一點,Async跟Await真正展現實力之處,在於搭配Background Task且平行處理運行。

簡單的Async跟Await寫法,能用上的地方其實極度的有限,例如按下某個按鈕,大概只能進行某個JSON的查詢,然後去更新單一個UI List,在這段等待的時間讓出一些CPU去進行Windows UI的相關繪製,大概就只能這樣了。

相關參考:

Async Await ASP.NET UX UI BackgroundTask