async和await的使用总结 ~ 居然一直用错了c#中的async和await的使用。。

对于c#中的async和await的使用,没想到我一直居然都有一个错误。。编程

。。仍是总结太少,这里记录下。c#

这里以作早餐为例

流程以下:api

  1. 倒一杯咖啡。
  2. 加热平底锅,而后煎两个鸡蛋。
  3. 煎三片培根。
  4. 烤两片面包。
  5. 在烤面包上加黄油和果酱。
  6. 倒一杯橙汁。

当使用同步方式实现时,代码是这样的:并发

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    class Program
    {
        static void Main(string[] args)
        {
            var sw = new Stopwatch();
            sw.Start();
 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!");

            Console.WriteLine($"totol time:{sw.ElapsedMilliseconds/1000}");
            Console.ReadKey();
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static Toast ToastBread(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        private static Bacon FryBacon(int slices)
        {
            Console.WriteLine($"putting {slices} slices of bacon in the pan");
            Console.WriteLine("cooking first side of bacon...");
            Task.Delay(3000).Wait();
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("flipping a slice of bacon");
            }
            Console.WriteLine("cooking the second side of bacon...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static Egg FryEggs(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            Task.Delay(3000).Wait();
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Put eggs on plate");

            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
    class Coffee { }
    class Egg { }
    class Bacon { }
    class Toast { }
    class Juice { }
}

运行效果以下:异步

或表示为这样async

同步准备的早餐大约花费了 30 分钟,由于总耗时是每一个任务耗时的总和。这里的total time只是用来表示记录下程序运行的时间。ide

而我之前写的异步代码是这样的:异步编程

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    class Program
    {
        static async void Main(string[] args)
        {
            var sw = new Stopwatch();
            sw.Start();
            Coffee cup = PourCoffee();
            Console.WriteLine("coffee is ready");

 Egg eggs = await FryEggsAsync(2); Console.WriteLine("eggs are ready");

 Bacon bacon = await FryBaconAsync(3); Console.WriteLine("bacon is ready");

 Toast toast = await ToastBreadAsync(2); ApplyButter(toast); ApplyJam(toast); Console.WriteLine("toast is ready");

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");

            Console.WriteLine($"totol time:{sw.ElapsedMilliseconds/1000}");
            Console.ReadKey();
        }

        static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
        {
            var toast = await ToastBreadAsync(number);
            ApplyButter(toast);
            ApplyJam(toast);

            return toast;
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static async Task<Toast> ToastBreadAsync(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            await Task.Delay(3000);
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        private static async Task<Bacon> FryBaconAsync(int slices)
        {
            Console.WriteLine($"putting {slices} slices of bacon in the pan");
            Console.WriteLine("cooking first side of bacon...");
            await Task.Delay(3000);
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("flipping a slice of bacon");
            }
            Console.WriteLine("cooking the second side of bacon...");
            await Task.Delay(3000);
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static async Task<Egg> FryEggsAsync(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            await Task.Delay(3000);
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            await Task.Delay(3000);
            Console.WriteLine("Put eggs on plate");

            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }

    }
    class Coffee { }
    class Egg { }
    class Bacon { }
    class Toast { }
    class Juice { }
}

效果以下:ui

 能够看出,这样编写的异步和最初同步版本的总共的耗时大体相同。spa

这是由于这段代码尚未利用异步编程的某些关键功能。

即上面的异步代码的使用在这里是不许确的。

能够看出,这段代码里面的打印输出与同步是同样的。

这是由于:在煎鸡蛋或培根时,此代码虽然不会阻塞,可是此代码也不会启动任何其余任务。

就形成了异步煎鸡蛋的操做完成后,才会开始培根制做。

可是,对于这里而言,我不但愿每一个任务都按顺序依次执行。

最好是首先启动每一个组件任务,而后再等待以前任务的完成。

例如:首先启动鸡蛋和培根。

同时启动任务

在不少方案中,你可能都但愿当即启动若干独立的任务。而后,在每一个任务完成时,你能够继续

进行已经准备的其余工做。

就像这里同时启动煎鸡蛋,培根和烤面包。

咱们这里对早餐代码作些更改。

正确的作法

第一步是存储任务以便在这些任务启动时进行操做,而不是等待:

Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");

Task<Egg> eggsTask = FryEggsAsync(2);
Egg eggs = await eggsTask;
Console.WriteLine("eggs are ready");

Task<Bacon> baconTask = FryBaconAsync(3); Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready");

Task<Toast> toastTask = ToastBreadAsync(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> eggsTask = FryEggsAsync(2); Task<Bacon> baconTask = FryBaconAsync(3); Task<Toast> toastTask = ToastBreadAsync(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 eggsTask;
Console.WriteLine("eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready");

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

运行效果以下:

 或者

 能够看出,这里一次启动了全部的异步任务。而你仅在须要结果时,才会等待每项任务

这里异步准备的形成大约花费20分钟,这是由于一些任务能够并发进行。

而对于直接  Egg eggs = await FryEggsAsync(2); 的方式,适用于你只须要等待这一个异步操做结果,不须要进行其余操做的时候。

与任务组合

吐司操做由异步操做(烤面包)和同步操做(添加黄油和果酱)组成。

这里涉及到一个重要概念:

异步操做后跟同步操做的这种组合也是一个异步操做。

也就是说,若是操做的任何部分是异步的,整个操做就是异步的。

代码以下:

static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
    var toast = await ToastBreadAsync(number); ApplyButter(toast); ApplyJam(toast); return toast;
}

全部,主要代码块如今变为:

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!");
}

高效的等待任务

能够经过使用Task类的方法改进上述代码末尾一系列await语句。

WhenAll 是其中的一个api , 它将返回一个其参数列表中的全部任务都已完成时猜完成的Task,

代码以下

await Task.WhenAll(eggsTask, 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>。

var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
    Task finishedTask = await Task.WhenAny(breakfastTasks); if (finishedTask == eggsTask)
    {
        Console.WriteLine("eggs are ready");
    }
    else if (finishedTask == baconTask)
    {
        Console.WriteLine("bacon is ready");
    }
    else if (finishedTask == toastTask)
    {
        Console.WriteLine("toast is ready");
    }
    breakfastTasks.Remove(finishedTask);
}

处理已完成任务的结果以后,能够从传递给 WhenAny 的任务列表中删除此已完成的任务。

 进行这些更改后,代码的最终版本将以下所示:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    class Program
    {
        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 breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
            while (breakfastTasks.Count > 0)
            {
                Task finishedTask = await Task.WhenAny(breakfastTasks); if (finishedTask == eggsTask)
                {
                    Console.WriteLine("eggs are ready");
                }
                else if (finishedTask == baconTask)
                {
                    Console.WriteLine("bacon is ready");
                }
                else if (finishedTask == toastTask)
                {
                    Console.WriteLine("toast is ready");
                }
                breakfastTasks.Remove(finishedTask);
            }

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");
        }

        static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
        {
            var toast = await ToastBreadAsync(number);
            ApplyButter(toast);
            ApplyJam(toast);

            return toast;
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static async Task<Toast> ToastBreadAsync(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            await Task.Delay(3000);
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        private static async Task<Bacon> FryBaconAsync(int slices)
        {
            Console.WriteLine($"putting {slices} slices of bacon in the pan");
            Console.WriteLine("cooking first side of bacon...");
            await Task.Delay(3000);
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("flipping a slice of bacon");
            }
            Console.WriteLine("cooking the second side of bacon...");
            await Task.Delay(3000);
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static async Task<Egg> FryEggsAsync(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            await Task.Delay(3000);
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            await Task.Delay(3000);
            Console.WriteLine("Put eggs on plate");
            
            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
}

效果以下:

或者

 

 这种异步的代码实现最终大约花费15分钟,由于一些任务能同时运行,

而且该代码可以同时监视多个任务,只在须要时才执行操做。

总结:

async 和 await的功能最好能作到:

尽量启动任务,不要在等待任务完成时形成阻塞。

便可以先把任务存储到task,而后在后面须要用的时候,调用await task()方法。

参考网址:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

相关文章
相关标签/搜索