c#中的Task異步編程

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index翻譯web

1. 引入編程

  Task異步編程模型(TAP)提供了對異步代碼的抽象,將代碼做爲語句序列,能夠在每一個階段完成下個階段開始前讀取代碼,該過程當中,編譯器進行了屢次轉換,由於一些語句可能啓動工做並返回正在進行的工做任務。異步

  Task異步編程的目標就是,啓動相似於語句序列的代碼,但當任務執行完成時,基於外部資源分配以一個更復雜的順序執行任務,相似於人們如何爲包含異步任務的進程發出指令。async

2. 異步編程ide

  在本文中,經過一個製做早餐的示例,瞭解關鍵字async和await關鍵字如何使得包含一系列異步指令的操做更容易。異步編程

  製造早餐的列表以下:微服務

  (1)倒一杯咖啡;ui

  (2)將鍋加熱,而後煎兩個雞蛋;spa

  (3)炒三片培根;翻譯

  (4)吐司兩片面包;

  (5)加入黃油和果醬吐司;

  (6)倒一杯橙汁

  烹飪早餐是異步工做的一個很好範例,同一我的能夠在一個步驟完成以前去執行另外一個步驟。該操做的同步代碼簡易版以下:

static void Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");
    Egg eggs = FryEggs(2);
    Console.WriteLine("eggs are ready");
    Bacon bacon = FryBacon(3);
    Console.WriteLine("bacon is ready");
    Toast toast = ToastBread(2);
    ApplyButter(toast);
    ApplyJam(toast);
    Console.WriteLine("toast is ready");
    Juice oj = PourOJ();
    Console.WriteLine("oj is ready");

    Console.WriteLine("Breakfast is ready!");
}

  若是採用上述給出的步驟進行早餐準備,整個效率會很是低下,而事實上,咱們能夠在鍋加熱煎雞蛋的過程當中,炒培根,在培根開始以後,就能夠將麪包放入烤麪包機。要想實現動做的異步執行,須要編寫異步代碼。異步實現的簡易代碼以下:

static async void Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");
    Egg eggs =await FryEggs(2);
    Console.WriteLine("eggs are ready");
    Bacon bacon =await FryBacon(3);
    Console.WriteLine("bacon is ready");
    Toast toast =await ToastBread(2);
    ApplyButter(toast);
    ApplyJam(toast);
    Console.WriteLine("toast is ready");
    Juice oj = PourOJ();
    Console.WriteLine("oj is ready");

    Console.WriteLine("Breakfast is ready!");
}

  此時,煎雞蛋、炒培根和烤麪包這三個動做就不須要依次執行,當烹飪雞蛋或培根時,代碼不會阻止,能夠同時啓動多個組件任務。

2.1 同時啓動任務

  許多狀況下,咱們但願當即啓動多個獨立任務,而後,當每一個任務完成後,能夠繼續其餘已準備好的工做。在上述早餐實例中,也就是要求更快的完成早餐。.NET Core中,System.Threading.Tasks.Task和相關類能夠用來推理正在進行的任務類,該特性使得更容易編寫接近實際建立早餐方式的代碼。可以同時開始烹飪雞蛋、培根和吐司。當每一個動做須要執行時,咱們能夠把注意力轉移到該任務上,注意下一個動做,而後等待其餘須要注意的事情。

  咱們能夠啓動一個任務並保留該工做的Task對象,await在處理結果以前,咱們將完成每項任務。對上述建立早餐的代碼進行修改,第一步是在操做開始時存儲操做,而非等待它們。

Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Task<Egg> eggTask=FryEggs(2);
Egg eggs=await eggTask;
Console.WriteLine("eggs are ready");
Task<Bacon> baconTask=FryBacon(3);
Bacon bacon=await baconTask;
Console.WriteLine("bacon is ready");
Task<Toast> toastTask=ToastBread(2);
Toast toast=await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");

Console.WriteLine("Breakfast is ready!");

  接下來,能夠將await在提供早餐前將炒培根和煎雞蛋語句移至末尾,代碼以下:

Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Task<Egg> eggTask = FryEggs(2);
Task<Bacon> baconTask = FryBacon(3);
Task<Toast> toastTask = ToastBread(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");

Egg eggs = await eggTask;
Console.WriteLine("eggs are ready");
Task<Bacon> baconTask = FryBacon(3);
Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready");

  該代碼的效果更好,能夠當即啓動全部的異步任務,只有在須要結果時纔等待每項任務。該代碼的實現相似於web應用程序中的代碼,可以發出不一樣微服務的請求,而後將結果組合成單個頁面。此時,咱們將當即發出全部的請求,而後await全部的任務並組合成web頁面。

2.2 任務組合  

  上述製做早餐的過程當中,製做吐司是異步操做(烤麪包)和同步操做(添加黃油和果醬)的組合。此時,咱們須要知道,異步操做和後續同步操做的組合是異步操做,即若是操做的任意部分是異步的,則整個操做都是異步的。

  下面給出建立工做組合的方法。在供應早餐以前,若是想要在添加黃油和果醬以前等待烘烤麪包的任何,則可使用如下代碼表示:

async Task<Toast> makeToastWithButterAndJamAsync(int number){
      var plainToast=await ToastBreadAsync(number);  
      ApplyButter(plainToast);
      ApplyJsm(plainToast);
      return plainToast;
}

  上述方法中包含了一個await語句,包含異步操做,該方法表明了烘烤麪包的任務,而後添加黃油和果醬,以後返回一個Task<TResult>,表示這三個操做的組合結果。當前代碼課修改成:

static async Task Main(string[] args){
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");
    var eggsTask = FryEggsAsync(2);
    var baconTask = FryBaconAsync(3);
    var toastTask = makeToastWithButterAndJamAsync(2);  
    var eggs = await eggsTask;
    Console.WriteLine("eggs are ready");
    var bacon = await baconTask;
    Console.WriteLine("bacon is ready");
    var toast = await toastTask;
    Console.WriteLine("toast is ready");
    Juice oj = PourOJ();
    Console.WriteLine("oj is ready");

    Console.WriteLine("Breakfast is ready!");

    async Task<Toast> makeToastWithButterAndJamAsync(int number)
    {
        var plainToast = await ToastBreadAsync(number);
        ApplyButter(plainToast);
        ApplyJam(plainToast);
        return plainToast;
    }

}

  以上代碼的修改說明了異步代碼工做的重要性,經過將操做分離爲返回任務的新方法來組合任務,能夠選擇什麼時候等待這項任務,同時啓動其餘任務

2.3 有效地等待其餘任務

  await能夠經過使用Task類的方法來該井前面代碼末尾的一系列語句,其中一個API是WhenAll,它返回一個在其參數列表中全部任務完成時完成的Task,如如下代碼所示:

await Task.WhenAll(eggTask,baconTask,toastTask);
Console.WriteLine("eggs are ready");
Console.WriteLine("bacon is ready");
Console.WriteLine("toast is ready");
Console.WriteLine("Breakfast is ready!");

  另外一個選擇是使用WhenAny,用它修飾的任務在任何參數完成時都返回一個Task<Task>,咱們在知道任務已經完成時,能夠等待返回的結果。如下代碼顯示瞭如何使用WhenAny等待第一個任務完成而後處理其結果,處理完結果後,從傳遞給的任務列表中刪除該已完成的任務。

var allTasks=new List<Task>{aggsTask,baconTask,toastTask};
while(allTask.Any()){
    Task finished=await Task.WhenAny(allTasks);
     if (finished == eggsTask)
    {
        Console.WriteLine("eggs are ready");
        allTasks.Remove(eggsTask);
        var eggs = await eggsTask;
    } else if (finished == baconTask)
    {
        Console.WriteLine("bacon is ready");
        allTasks.Remove(baconTask);
        var bacon = await baconTask;
    } else if (finished == toastTask)
    {
        Console.WriteLine("toast is ready");
        allTasks.Remove(toastTask);
        var toast = await toastTask;
    } else
            allTasks.Remove(finished);
}
Console.WriteLine("Breakfast is ready!");    
 

  在全部更改後,最終版本main方法以下:

static async Task Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");
    var eggsTask = FryEggsAsync(2);
    var baconTask = FryBaconAsync(3);
var toastTask = makeToastWithButterAndJamAsync(2);
var allTasks = new List<Task>{eggsTask, baconTask, toastTask};
while(allTask.Any()){
 Task finished = await Task.WhenAny(allTasks);
if (finished == eggsTask)
        {
            Console.WriteLine("eggs are ready");
            allTasks.Remove(eggsTask);
            var eggs = await eggsTask;
        } else if (finished == baconTask)
        {
            Console.WriteLine("bacon is ready");
            allTasks.Remove(baconTask);
            var bacon = await baconTask;
        } else if (finished == toastTask)
        {
            Console.WriteLine("toast is ready");
            allTasks.Remove(toastTask);
            var toast = await toastTask;
        } else
                allTasks.Remove(finished);
}
Console.WriteLine("Breakfast is ready!");

    async Task<Toast> makeToastWithButterAndJamAsync(int number)
    {
        var plainToast = await ToastBreadAsync(number);
        ApplyButter(plainToast);
        ApplyJam(plainToast);
        return plainToast;
    }
}
相關文章
相關標籤/搜索