栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

【6.使用 Async 和 Await 的异步编程】

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【6.使用 Async 和 Await 的异步编程】

c#编程,c#基础,异步编程

文章目录
  • 一、简介
  • 二、上面中文用c#表示
  • 三、不要阻塞,而要 await
  • 四、同时启动任务
  • 五、与任务组合
  • 六、异步异常
  • 七、高效地等待任务


一、简介
  • 基于任务的异步编程模型 (TAP) 提供了异步代码的抽象化。 你只需像往常一样将代码编写为一连串语句即可。 就如每条语句在下一句开始之前完成一样,你可以流畅地阅读代码。 编译器将执行许多转换,因为其中一些语句可能会开始运行并返回表示正在进行的工作的 Task。

如何做早餐Demo
1倒一杯咖啡。
2加热平底锅,然后煎两个鸡蛋。
3煎三片培根。
4烤两片面包。
5在烤面包上加黄油和果酱。
6倒一杯橙汁。
如果你有烹饪经验,便可通过异步方式执行这些指令。 你会先开始加热平底锅以备煎蛋,接着再从培根着手。 你可将面包放进烤面包机,然后再煎鸡蛋。 在此过程的每一步,你都可以先开始一项任务,然后将注意力转移到准备进行的其他任务上。

二、上面中文用c#表示
using System;
using System.Threading.Tasks;

namespace ConsoleApp7
{
    public class Program
    {
        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!");

            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");
        /// 
        /// 烤面包   slices 片
        /// 
        /// 
        /// 
       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");
            // toast烤面包
            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();
        }
    }
    public class Coffee
    {

    }
    public class Egg
    {

    }
    /// 
    /// 培根
    /// 
    public class Bacon
    {

    }
    /// 
    /// 烤面包片
    /// 
    public class Toast
    {

    }
    /// 
    /// 果汁
    /// 
    public class Juice
    {

    }
}

计算机不会按人类的方式来解释这些指令。 计算机将阻塞每条语句,直到工作完成,然后再继续运行下一条语句。 这将创造出令人不满意的早餐。 后续任务直到早前任务完成后才会启动。 这样做早餐花费的时间要长得多,有些食物在上桌之前就已经凉了。

如果你希望计算机异步执行上述指令,则必须编写异步代码。

成功的现代应用程序需要异步代码。 在没有语言支持的情况下,编写异步代码需要回调、完成事件,或其他掩盖代码原始意图的方法。 同步代码的优点是,它的分步操作使其易于扫描和理解。 传统的异步模型迫使你侧重于代码的异步性质,而不是代码的基本操作。

三、不要阻塞,而要 await

await 关键字提供了一种非阻塞方式来启动任务,然后在此任务完成时继续执行。await启动任务

static async Task Main(string[] args)
{
    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!");
}

提示
FryEggsAsync、FryBaconAsync 和 ToastBreadAsync 的方法主体都已更新,现会分别返回 Task、Task 和 Task。 这些方法的名称与其原始版本不同,将包含“Async”后缀。 它们的实现在本文的稍后部分显示为最终版本的一部分。

在煎鸡蛋或培根时,此代码不会阻塞。 不过,此代码也不会启动任何其他任务。 你还是会将面包放进烤面包机里,然后盯着烤面包机直到面包弹出。 但至少,你会回应任何想引起你注意的人。 在接受了多份订单的一家餐馆里,厨师可能会在做第一份早餐的同时开始制作另一份早餐。

现在,在等待任何尚未完成的已启动任务时,处理早餐的线程将不会被阻塞。 对于某些应用程序而言,此更改是必需的。 仅凭借此更改,GUI 应用程序仍然会响应用户。 然而,对于此方案而言,你需要更多的内容。 你不希望每个组件任务都按顺序执行。 最好首先启动每个组件任务,然后再等待之前任务的完成。

四、同时启动任务

在许多方案中,你希望立即启动若干独立的任务。 然后,在每个任务完成时,你可以继续进行已准备的其他工作。 在早餐类比中,这就是更快完成做早餐的方法。 你也几乎将在同一时间完成所有工作。 你将吃到一顿热气腾腾的早餐。

System.Threading.Tasks.Task 和相关类型是可以用于推断正在进行中的任务的类。

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

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

Task baconTask = FryBaconAsync(3);
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
// Task 你返回什么我都能接收,因为我是泛型
Task 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!");
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");

Task eggsTask = FryEggsAsync(2);
Task baconTask = FryBaconAsync(3);
Task 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 分钟,由于一些任务并发运行,因此节约了时间。

五、与任务组合

除了吐司外,你准备好了做早餐的所有材料。 吐司制作由异步操作(烤面包)和同步操作(添加黄油和果酱)组成

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

    return toast;
}

上述方式的签名中具有 async 修饰符。 它会向编译器发出信号,说明此方法包含 await 语句;也包含异步操作。 此方法表示先烤面包,然后再添加黄油和果酱的任务。 此方法返回表示这三个操作的组合的 Task。 主要代码块现在变成了

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 中。 Task.Exception 属性为 System.AggregateException,因为异步工作期间可能会引发多个异常。 引发的任何异常都将添加到 AggregateException.InnerExceptions 集合中。 如果该 Exception 属性为 NULL,则将创建一个新的 AggregateException 且引发的异常是该集合中的第一项。

七、高效地等待任务

返回一个其参数列表中的所有任务都已完成时才完成的 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

var breakfastTasks = new List { 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);
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/910994.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号