zoukankan      html  css  js  c++  java
  • .NET 中 async 和 await

    前言

    C# 中的 Async 和 Await 关键字是异步编程的核心。使用这两个关键字可以轻松创建异步方法。使用 async 关键字定义的异步方法简称“异步方法”。

    异步编程

    并发的一种形式,它采用furture模式或回调(callback)机制,以避免产生不必要的线程。.Net中future的类型有 TaskTask<Result>

    异步编程的核心理念是异步操作:

    启动了的操作将会在一段时间后完成。

    这个操作正在执行时,但不会阻塞原来的线程。

    启动了这个操作的线程后,可以继续执行其它任务。

    当操作完成时,它会通知它的future,或者调用回调函数,以便让程序知道操作已经结束。

    异步的好处

    1. 对于面向终端用户的GUI程序,异步可以提高响应能力。

    2. 对于服务器应用:异步编程实现了可扩展。

      服务器可以利用线程池填满其可扩展性,使用异步编程后,可扩展性通常可以提供一个数量级,可以最大程度的压榨服务器性能,提高处理能力。

    async

    使用 async 修饰符可以将方法、lambda表达式或匿名方法指定为异步。

    async 的主要目的是,使方法内的await关键字生效。

    //等待异步完成再执行后边的操作,但是整个方法不会阻塞
    var result = await DoSomethingAsync();
    output.Result = result;
    

    如果使用了 Async 最好一直使用它

    await

    async 标记的异步方法,可以使用 await 来指定暂定点。 await 运算符通知编译器异步方法:在等待的异步过程完成后才能继续通过该点。同时,会将控制权返回至异步方法的调用方

    async 方法在开始时以同步的方式执行。在 async 方法内部,await 关键字对他的参数(一个异步任务)执行一个异步等待。它首先检查操作是否已经完成,如果完成了,就继续运行(同步方法)。否则,他会暂停 async 方法,并返回,将控制权交给调用方,留下一个 未完成的 Task。一段时间后,操作完成,async方法再恢复运行。

    await 语句等待一个任务完成,当该方法在 await 处暂停时,就可以捕捉上下文(context)。如果当前SynchronizationContext不为空,这个上下文就是当前SynchronizationContext。如果当前SynchronizationContext为空,则这个上下文为当前TaskScheduler。该方法会在这个上下文中继续运行。

    //此时await会捕获当前上下文
    await DoSomethingAsync();
    //....    //这里会试图用上边捕获的上下文继续执行
    
    await DoSomethingAsync().ConfigureAwait(false);
    //.... 这里开始在新的线程中运行
    

    ConfigureAwait 配置 Taskawaiter,将延续任务封装回原始上下文,则为True ,否则为 False

    详情可查阅ConfigureAwait(false)资料,这里暂时不做赘述。可阅读以下文章

    异步方法异常:

    异步方法异常时会返回在 Task 对象中,并将这个 Task 对象的状态改变为“已完成”。当 await 调用该 Task 对象时,await 会获得并(重新)抛出该异常,并保留原始的栈轨迹。

    注意:

    异步方法避免使用 Task.WaitTask<T>.Result ,因为他们会导致死锁。

    示例:

    public async Task<int> GetUrlContentLengthAsync()
    {
        var client = new HttpClient();
        
        //异步执行请求,立即返回一个Task<string>,并将控制权让出
        Task<string> getStringTask =
            client.GetStringAsync("https://docs.microsoft.com/dotnet");
        
        //由于异步方法未执行等待,所以可以继续执行不依赖异步返回结果的同步方法
        DoIndependentWork();
        
        //挂起任务进度,并将控制权交割GetUrlContentLengthAsync方法的调用方,并返回一个Task<int>给调用方。
        //该任务表示将返回下载字符串长度的一个承诺
        //然后调用方会继续执行,执行不依赖于GetUrlContentLengthAsync返回结果的其它工作,否则就等待。
        string contents = await getStringTask;
        return contents.Length;
    }
    
    void DoIndependentWork()
    {
        Console.WriteLine("Working...");
    }
    

    await 运算符会暂停 GetUrlContentLengthAsync 方法:

    • getStringTask 完成之前,GetUrlContentLengthAsync 无法继续。
    • 同时,控件返回至 GetUrlContentLengthAsync 的调用方。
    • getStringTask 完成时,控件将在此继续。
    • 然后,await 会从 getStringTask 检索 string 结果

    如果 DoIndependentWork 依赖于异步执行的结果,则在等待 getStringTask 返回结果期间不能进行任何工作。需要改成以下写法。

    string getStringTask = await client.GetStringAsync("https://docs.microsoft.com/dotnet");
    

    构成异步方法的条件:

    1. 方法签名要包含 async 修饰符。
    2. 按照约定,异步方法的名称以“Async”后缀结尾。
    3. 返回类型为以下类型之一
      1. 如果你的方法有返回值,则返回 Task<Result> 的类型。
      2. 如果你的方法没有返回值,则返回 Task 类型
    4. 方法中至少要包含一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,并且将控制权返回到方法的调用方。

    返回类型

    await 运算符的操作数通常是以下几种.NET类型:Task、Task<TResult>、ValueTask或VauleTask<TResult>。但是任何可等待表达式都可以是await运算符的操作数。

    总结

    1. 异步可以提高响应能力。
    2. 异步不会阻塞线程
    3. 使用 async 来标记异步方法
    4. 使用 await 来指定暂停点,挂起其进度,在等待的异步过程完成后才能继续通过该点。同时,会将控制权返回至异步方法的调用方,调用方可以继续执行不依赖于异步返回结果的其它工作。
    5. 如果使用了 Async 最好一直使用它
    6. 异步方法避免使用 Task.WaitTask<T>.Result ,因为他们会导致死锁。

  • 相关阅读:
    【面试突击】-RabbitMQ常见面试题(一)
    并发艺术--java并发编程基础
    并发艺术--java内存模型
    并发艺术--java并发机制的底层实现原理
    并发艺术--并发编程挑战
    Spring Boot 项目中的 parent
    封装关于金额计算的double工具类
    日期和字符串类型相互转换工具类
    统一封装json返回结果
    Hibernate-validator数据验证
  • 原文地址:https://www.cnblogs.com/imlxp/p/14286462.html
Copyright © 2011-2022 走看看