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;
    }
}
相关文章
相关标签/搜索