天天看點

基于任務的異步程式設計(Task,async,await)

這節講一下比較進階的異步程式設計用法Task,以及兩個異步關鍵字async和await。

    Task是在C#5.0推出的文法,它是基于任務的異步程式設計文法,是對Thread的更新,也提供了很多API,先看一下Task怎麼使用:

System.Threading.Tasks.Task.Run(() =>
  {
      Console.WriteLine("異步");
  });
  System.Threading.Tasks.Task aTask=new System.Threading.Tasks.Task(() =>
  {
      Console.WriteLine("異步");
  });
  aTask.Start();
  System.Threading.Tasks.Task.Factory.StartNew(() =>
  {
      Console.WriteLine("異步");
  });      

Task.Run()可以直接異步運作一個方法,或者使用執行個體化Task傳入委托的方式,通過start()進行啟動,再或者使用Task.Factory.StartNew()直接啟動。

async,await

    為了進一步介紹Task,需要先介紹兩個異步有關的關鍵字async,await

    async用在方法的聲明,await用于代碼語句中。被async标記的方法,稱作異步方法。但是,并非整個方法都是異步執行,代碼中以await開頭标記的代碼,才是要真正異步執行的具體内容。這個關鍵字一般是配合Task來使用的,Task有泛型的形式,辨別異步的傳回值類型,通過Result()方法擷取傳回值。這段說明很難了解,下面看代碼示範:

static void Main(string[] args)
{
    Console.WriteLine("建立一個異步任務");
    Task<int> task = new Program().GetValue();
​
    Console.WriteLine("正在計算結果....");
    Console.WriteLine($"運作結果為:{task.Result}");
    Console.WriteLine("任務完成....");
}
public async Task<int> GetValue()
{
    Console.WriteLine("即将開始進行計算...3");
    Console.WriteLine("即将開始進行計算...2");
    Console.WriteLine("即将開始進行計算...1");
    int a= await System.Threading.Tasks.Task.Run(() =>
    {
        int i = 0;
         for (; i < 10; i++)
         {
             Console.WriteLine(i);
         }
         return i;
     });
    Console.WriteLine("結果計算完成....");
    return a;
}      

 運作結果為:

基于任務的異步程式設計(Task,async,await)

從運作結果可以看出,程式運作到15行await處後,下一步就跳出了這個方法,回到第6行執行,這也是await的一個特性,異步執行,将主線程執行權交回,也就是說,從15行到25行是在背景線程中執行的,之前的執行都是同步的,之後的執行也是同步的,而且,主線程的腳步沒有停下,直到遇到task.Result,Result裡邊存放着異步方法運作的傳回值,運作到這,如果異步沒有完成,就會阻塞目前線程,直到異步傳回結果。

    另外說一點,之前在講自定義中間件的時候,涉及到過這兩個關鍵詞,現在明白了這個用法,可以回去再看一下,應該會對中間件的通路流程有一個更清晰的了解。

    ContinueWith

    ContinueWith設定Task在執行完原有任務後,再繼續執行此方法設定的方法,下面看代碼:

task.ContinueWith((task) =>
  {
      Console.WriteLine("---------------"+a.Result);
  });      

 這是其中的一個重載,接受一個Action<Task<T>>類型的委托,此處乍一看可能會不解,其實就是把目前執行任務的Task對象傳進來了。這樣的用法有什麼好處呢,運作完了以後,可以直接取Task任務的傳回值,不用阻塞線程,當然這是在傳回值不是急需的情況下。

CancellationTokenSource

    CancellationTokenSource類用于終止一個任務,請先看一下代碼:

static void Main(string[] args)
{
    CancellationTokenSource cancellationToken=new CancellationTokenSource();
​
    Task<int> task= new Program().GetValue(cancellationToken);
    cancellationToken.Cancel();
    Console.WriteLine("正在計算結果....");
​
    Console.WriteLine($"運作結果為:{task.Result}");
    Thread.Sleep(1000);
    Console.WriteLine("任務完成....");
}
public Task<int> GetValue(CancellationTokenSource cancellationToken)
{
    Task<int> a = System.Threading.Tasks.Task.Run(() =>
     {
         int i = 0;
         for (; i < 100; i++)
         {
             Console.WriteLine(i);
             Thread.Sleep(500);
         }
         return i;
     },cancellationToken.Token);
    Console.WriteLine("結果計算完成....");
    return a;
}      

建立一個CancellationTokenSource對象,在Run任務的時候傳入一個Token,就能調用Cancel()方法就能終止這個任務,運作結果為:

基于任務的異步程式設計(Task,async,await)

 可以看到報錯了,這很正常,因為任務停止了,顯然Result是沒有值的

    最後注意一點,異步不是多線程,可以說異步是基于多線程,但是它們不是等于的關系。

繼續閱讀