【異步編程】Part1:await&async語法糖讓異步編程如魚得水

前導

  Asynchronous programming Model(APM)異步編程模型以BeginMethod(...) 和 EndMethod(...)結對出現。
IAsyncResult  BeginGetResponse(AsyncCallback callback, object state)
WebResponse  EndGetResponse(IAsyncResult asyncResult)

   Event-based Asynchronous Pattern(EAP)模型以MethodAsync(...) 和CancelAsync(...) 結對出現,由Completed事件設置回調函數。數據庫

WebClient類中經過DownloadStringAsync方法開啓一個異步任務,並由DownloadStringCompleted事件供設置回調函數,能經過CancelAsync方法取消異步任務。編程

  .Net4.5開始Task Parallel Library(TPL) 爲異步和並行編程提供新的模型,使異步和併發操做有統一的編程入口,網絡

該模型常定義以Async後綴結尾的函數名、返回帶有awaitable屬性的Task/Task<T>對象, 若是你的program target設置爲4.5+,可用Task-based Asynchronous Pattern (TAP)取代以上2種模型。架構

 

TAP

  TPL的核心是Task類,Task,Task<TResult>能夠將其理解爲一個包裝委託(一般就是Action或Func委託)並執行的容器,有了Task幾乎不用去和Thread 打交道,使用者只須要關注具體業務對應的Job,Task背後有一個 TaskScheduler 的類來負責調度Task的執行,這樣Task對象將在默認的TaskScheduler調度下執行,TaskScheduler使用線程池中的線程,至因而新建仍是使用已有線程這個對用戶是徹底透明的,也能夠經過重載函數的參數傳入自定義的TaskScheduler。併發

Task任務狀態:異步

 狀態& 枚舉值  說明
 Created = 0  The task has been initialized but has not yet been scheduled
 WaitingForActivation = 1  The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
 WaitingToRun = 2  The task has been scheduled for execution but has not yet begun executing.
 Running = 3  The task is running but has not yet completed.
 WaitingForChildrenToComplete = 4  The task has finished executing and is implicitly waiting for attached child tasks to complete.
 RanToCompletion = 5  The task completed execution successfully
Canceled = 6  The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken while the token was in signaled state, or the task's CancellationToken was already signaled before the task started executing
 Faulted = 7  The task completed due to an unhandled exception

明確Task和線程的關係:async

  • 任務是架構在線程之上的,也就是說任務最終仍是要拋給線程去執行
  • 任務跟線程不是一對一的關係,好比開10個任務並非說會開10個線程,在.NET面向任務異步編程模型中,你只須要關注業務概念的任務,具有底層實現會由Task包裝完成。
Task相比ThreadPool的優點:
  • ThreadPool不支持線程取消、完成、失敗通知等交互新操做
  • ThreadPool不支持線程執行的前後順序。

 

await/async 語法糖

在異步編程實踐中,將網絡、數據庫同步訪問稱爲 I/O-bound;將等待CPU計算結果稱爲CPU-boundide

  TAP異步編程模型的核心是塑造異步操做的Task、Task<T>對象,這是awaitable 對象,await/async語法糖簡化了寫法異步編程

  • 對於I/O-bound 代碼,編寫一個返回Task或Task<T>的async方法, 以後await 這個方法函數

  • 對於CPU-bound代碼,使用Task.Run方法後臺啓動一個操做,以後await這個操做。

  魔法發生在await關鍵字,會將控制權上交給執行Async方法的上層調用者。

  在C#語言底層,編譯器將你的await/async 代碼轉換爲狀態機, 記錄了當await發生時控制權上交後臺工做完成時恢復執行的標記。

 
  異步編程在實踐時須要理解:
  • 異步代碼可用於I/O -bound 和CPU-bound 代碼, 可是2個場景的寫法是不一樣的

  • 異步編程利用Task和Task<T>對象來 塑造須要在後臺完成的工做

  • async關鍵字將方法轉變爲異步方法,這樣可在方法體使用await關鍵詞, 若是async方法內不包含await關鍵詞,那將不會上交控制權

  • 當await動做發生時,將會暫停(注意是suspend 而不是block)方法,並將控制權上交給調用者(直到awaitable任務完成)

  • await 只能被用在async方法內部

執行操做的「異步方式」

 執行如下操做…  替換如下方式…  使用如下方式
 檢索後臺任務的結果  Task.Wait / Task.Result  await
 等待任何任務完成  Task.WaitAny  await Task.WhenAny
 檢索多個任務的結果  Task.WaitAll  await Task.WhenAll
 等待一段時間  Thread.Sleep  await Task.Delay


下面是一個I/O-bound的例子:    

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
namespace Test
{
    class Program
    {
        static void Main(string[] args)
       {
         var asyncMethod =  AccessTheWebAsync();
         Console.WriteLine("go on ...... "+ Thread.CurrentThread.ManagedThreadId );
         // 等待異步線程處理完畢,沒有如下句子,await使控制回到調用方,主線程即終止。
         asyncMethod.Wait();
       }
      public static async Task<int> AccessTheWebAsync()
      {
          HttpClient client = new HttpClient();
          // GetStringAsync returns a Task<string>.
          // That means that when you await the task you'll get a string (urlContents).
          Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
          // You can do work here that doesn't rely on the string from GetStringAsync.
         DoIndependentWork();

        // The await operator suspends AccessTheWebAsync.
        // - AccessTheWebAsync can't continue until getStringTask is complete.
        // - Meanwhile, control returns to the caller of AccessTheWebAsync.
        // - Control resumes here when getStringTask is complete.
        // - The await operator then retrieves the string result from getStringTask.
    
         string urlContents = await getStringTask;
         Console.WriteLine(urlContents.Length+"....... "+Thread.CurrentThread.ManagedThreadId );
         // The return statement specifies an integer result.
         // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
         return urlContents.Length;
       } 
        public static void DoIndependentWork()
        {
            Console.WriteLine("work ......"+Thread.CurrentThread.ManagedThreadId);
        }
    }
}

  以上代碼在ASP.NET 或GUI程序可能會發生死鎖, 具體參見《.NET異步編程系列3:掌握SynchronizationContext避免deadlock》;控制檯程序通過驗證在.NET Core 和.Net Framework上都沒有SynchronizationContext,故不會發生死鎖。

Task對象提供了豐富的API幫助咱們完成 基於任務的異步操做, 讓咱們專一業務概念的任務

 

做者: JulianHuang

感謝您的認真閱讀,若有問題請大膽斧正,若是您以爲本文對你有用,不妨右下角點個或加關注。

本文版權歸做者全部,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置註明本文的做者及原文連接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索