• 《C#并发编程经典实例》学习笔记—异步编程关键字 Async和Await


    C# 5.0 推出async和await,最早是.NET Framework 4.5引入,可以在Visual Studio 2012使用。在此之前的异步编程实现难度较高,async使异步编程的实现变得简便。

    各平台对async的支持情况

    平台 async
    .NET 4.5及以上
    .NET 4.0 NuGet
    Mono iOS/Droid
    Windows Store
    Windows Phone Apps 8.1
    Windows Phone SL 8.0
    Windows Phone SL 7.1 NuGet
    Silverlight 5 NuGet

    在不支持的平台,安装NuGet包 Microsoft.Bcl.Async

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

    async 对方法做了什么处理

    从使用async修饰符修饰的方法的IL代码可以得出一个结论:

    • 在Debug下,针对async方法,生成的是一个class状态机
    • 在Release下,针对async方法,生成的是一个struct状态机

    举例:
    C#代码如下

    using System.Threading.Tasks;
    
    namespace ConsoleApp3
    {
        public class Test
        {
            public async Task TestAsync()
            {
                await GetAsync();
            }
    
            public async Task GetAsync()
            {
                await Task.Delay(1);
            }
        }
    }
    

    以TestAsync方法为准

    Release下 初始化状态机V_0 ,类型是值类型Struct(valuetype),类型名称为<TestAsync>d__0

        .locals init (
          [0] valuetype ConsoleApp3.Test/'<TestAsync>d__0' V_0,
          [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
        )
    
    

    <TestAsync>d__0 继承值类型[mscorlib]System.ValueType

    .class nested private sealed auto ansi beforefieldinit 
        '<TestAsync>d__0'
          extends [mscorlib]System.ValueType
          implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine
    

    Debug 下 初始化状态机V_0 ,类型是引用类型Class(class) ,类型名称为<TestAsync>d__0

        .locals init (
          [0] class ConsoleApp3.Test/'<TestAsync>d__0' V_0,
          [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
        )
    

    <TestAsync>d__0 继承引用类型[mscorlib]System.Object

      .class nested private sealed auto ansi beforefieldinit 
        '<TestAsync>d__0'
          extends [mscorlib]System.Object
          implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine
    

    异步方法的定义和注意事项

    使用 async 关键字定义的异步方法简称为“异步方法”。

    注意事项:

    • 如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。 请参阅编译器警告(等级 1)CS4014
    • async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
    • 不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void
    • 一定要避免使用Task.WaitTask<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
    • 异步方法的参数不能使用outrefoutref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。

    异步方法的特征

    • 方法签名包含 async 修饰符。
    • 按照约定,异步方法的名称以“Async”后缀结尾。
    • 返回类型为下列类型之一:
      • 如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>
      • 如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task
      • void:如果要编写异步事件处理程序。
      • 包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
    • 方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。

    关于async和await具体的执行流程,方法何时挂起和释放,请参考异步程序中的控制流 (C#)

    异步返回类型

    上面提到 void 作为返回结果,适用于事件处理程序。
    举例:

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp3
    {
        public class TestVoidAsync
        {
            private event EventHandler<EventArgs> DoTest;
    
            public TestVoidAsync()
            {
                DoTest += DoTestEvent;
            }
    
            private static async void DoTestEvent(object sender, EventArgs e)
            {
                await Task.Delay(1000);
            }
    
            protected virtual void OnDoTest()
            {
                DoTest?.Invoke(this, EventArgs.Empty);
            }
        }
    }
    
    

    void 作为返回结果存在一个弊端:无法捕获异常。

    返回 void 的异步方法的调用方无法捕获从该方法引发的异常,且此类未经处理的异常可能会导致应用程序故障。 如果返回 TaskTask<TResult> 的异步方法中出现异常,此异常将存储于返回的任务中,并在等待该任务时再次引发。

    通用的异步返回类型:

    从 C# 7.0 开始,异步方法可返回任何具有可访问的 GetAwaiter 方法的类型。

    ValueTask<TResult>

    Task 和 Task<TResult> 是引用类型,因此,性能关键路径中的内存分配会对性能产生负面影响,尤其当分配出现在紧凑循环中时。 支持通用返回类型意味着可返回轻量值类型(而不是引用类型),从而避免额外的内存分配。

    使用ValueTask<TResult>,需要添加NuGet包 System.Threading.Tasks.Extensions

    ValueTask<TResult> 是struct值类型,Task 和 Task<TResult> 是class引用类型

    异步操作的生命周期

    Task 类提供了异步操作的生命周期,且该周期由 TaskStatus 枚举表示。

    状态 执行顺序 备注
    Created 0 该任务已初始化,但尚未安排。
    WaitingForActivation 1 该任务正在等待被.NET Framework infrastructure 内部激活和调度。
    WaitingToRun 2 该任务已安排执行但尚未开始执行。
    Running 3 任务正在运行但尚未完成。
    WaitingForChildrenToComplete 4 任务已完成执行,并隐式等待附加的子任务完成。
    RanToCompletion 5 任务已成功完成执行。
    Canceled 6 引发 OperationCanceledException 异常,或者在任务开始执行之前取消
    Faulted 7 由于未处理的异常,任务已完成。

    Canceled 和 Faulted状态都会因为任务异常导致转换为该状态。二者的区别如下:

    如果标记的 IsCancellationRequested 属性返回 false,或者异常的标记与任务的标记不匹配,则会将 OperationCanceledException 按照普通的异常来处理,从而导致任务转换为 Faulted 状态。 另外还要注意,其他异常的存在将也会导致任务转换为 Faulted 状态。 您可以在 Status 属性中获取已完成任务的状态。

    参考文章:

  • 相关阅读:
    一 基础--进制转化
    七牛云上传视频并截取第一帧为图片(js实现)
    FNScanner二维码接口openView自定义扫码Demo
    UIPickerView 模块示例demo
    vPlayer 模块Demo
    doT的高级用法及loadData的使用
    acmPush模块示例demo
    UIChatBox模块示例demo
    分享一款基于aui框架的图文发布界面
    基于js的APP多语言处理
  • 原文地址:https://www.cnblogs.com/AlienXu/p/9529541.html
Copyright © 2020-2023  润新知