C#异步编程async/await用法详解

异步函数简介

一般指 async 修饰符声明得、可包含await表达式得方法或匿名函数。

声明方式

异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字。async可以出现在返回值之前的任何位置, 如下示例:

        async public static void GetInfoAsync()
        {
           //...
        }

        public async static void GetInfoAsync()
        {
           //...
        }

        public static async void GetInfoAsync()
        {
            //...
        }

异步方法的返回类型

异步函数的返回类型只能为: void、Task、Task<TResult>。

Task<TResult>: 代表一个返回值T类型的操作。

Task: 代表一个无返回值的操作。

void: 为了和传统的事件处理程序兼容而设计。

await(等待)

await等待的是什么? 可以是一个异步操作(Task)、亦或者是具备返回值的异步操作(Task<TResult>)的值, 如下:

        public async static void GetInfoAsync()
        {
            await GetData(); // 等待异步操作, 无返回值
            await GetData<int>(1); //等待异步操作, 返回值 int
        }

        static Task GetData()
        {
            //...
            return null;
        }

        static Task<T> GetData<T>(int a)
        {
            //...
            return null;
        }

注: await 最终操作的是一个值, 当然, 也可以是无值, 如上GetData() , 否则就是一个 Task<T> 如上: GetData<T>()

await执行过程

TaskAwaiter 获取执行结果

一般而言, await等待的一个异步操作, 无论是具备返回值还是否, 那么最终都会获得该操作是否已完成、具备返回值得异步操作可以获取他得返回结果。

所以这个时候,TaskAwaiter出现了, 无论是Task、还是Task<TResult>操作, 都具备GetAwaiter() 方法。

用于获取改操作得状态、返回结果, 及部分操作, 如下TaskAwaiter结构:

    //
    // 摘要:
    //     提供等待异步任务完成的对象。
    public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
    {
        //
        // 摘要:
        //     获取一个值,该值指示是否已完成的异步任务。
        //
        // 返回结果:
        //     true 如果任务已完成;否则为 false。
        //
        // 异常:
        //   T:System.NullReferenceException:
        //     System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
        public bool IsCompleted { get; }

        //
        // 摘要:
        //     结束异步任务完成之前的等待。
        //
        // 异常:
        //   T:System.NullReferenceException:
        //     System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
        //
        //   T:System.Threading.Tasks.TaskCanceledException:
        //     任务已取消。
        //
        //   T:System.Exception:
        //     在完成的任务 System.Threading.Tasks.TaskStatus.Faulted 状态。
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public void GetResult();
        //
        // 摘要:
        //     设置时应执行的操作 System.Runtime.CompilerServices.TaskAwaiter 对象停止等待异步任务完成。
        //
        // 参数:
        //   continuation:
        //     要在等待操作完成时执行的操作。
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     continuation 为 null。
        //
        //   T:System.NullReferenceException:
        //     System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
        [SecuritySafeCritical]
        public void OnCompleted(Action continuation);
        //
        // 摘要:
        //     计划程序与此等待异步任务的延续任务操作。
        //
        // 参数:
        //   continuation:
        //     要等待操作完成时调用的操作。
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     continuation 为 null。
        //
        //   T:System.InvalidOperationException:
        //     该等待程序未正确初始化。
        [SecurityCritical]
        public void UnsafeOnCompleted(Action continuation);
    }

接下来, 演示如何通过等待去获取异步操作的返回结果, 如下代码所示:

        public async static void GetInfoAsync()
        {
            Task<bool> task = Task.Run<bool>(() =>
            {
                Thread.Sleep(10000); //模拟耗时
                return true;
            });

            //以下两种方式
            bool taskResult1 = await task;  //内部自己执行了GetAwaiter()
            bool taskResult = task.GetAwaiter().GetResult();  //自己手动执行Awaiter(), 但是阻塞UI
       Console.WriteLine(taskResult);
        }

注: 对于一个await表达式, 编译器生成的代码会先调用GetAwaiter(), 然后通过awaiter得成员来等待结果, 所以以上两种方式等效( 不考虑阻塞的情况下)

为了验证以上猜测, 通过反编译工具查看得到如下代码:

编译器最终生成两个密封类, 一个类( <>c )我们展开分析:

<GetInfoAsync>b__1_0() 正是模拟耗时的一个操作委托生成的方法。

        [CompilerGenerated]
        [Serializable]
        private sealed class <>c
        {
            public static readonly Program.<>c <>9 = new Program.<>c();
            public static Func<bool> <>9__1_0;
            internal bool <GetInfoAsync>b__1_0()
            {
                Thread.Sleep(10000);
                return true;
            }
        }

第二个类<GetInfoAsync>d__1 分析:

该类分别实现了接口 IAsyncStateMachine 的MoveNext() 与 SetStateMachine() ,另外 注意,

还特别定义了一个 <>t__builder, 先记住他, 下面讲会对他讲到, 为什么编译器生成的代码会默认先调用GetAwaiter()

 [CompilerGenerated]
         private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine
         {
             public int <>1__state;
             public AsyncVoidMethodBuilder <>t__builder;
             private Task<bool> <task>5__1;
             private bool <result>5__2;
             private bool <>s__3;
             private TaskAwaiter<bool> <>u__1;
             void IAsyncStateMachine.MoveNext()
             {
                 int num = this.<>1__state;
                 try
                 {
                     TaskAwaiter<bool> awaiter;
                     if (num != 0)
                     {
                         Func<bool> arg_2F_0;
                         if ((arg_2F_0 = Program.<>c.<>9__1_0) == null)
                         {
                             arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>9.<GetInfoAsync>b__1_0));
                         }
                         this.<task>5__1 = Task.Run<bool>(arg_2F_0);
                         awaiter = this.<task>5__1.GetAwaiter();
                         if (!awaiter.IsCompleted)
                         {
                             this.<>1__state = 0;
                             this.<>u__1 = awaiter;
                             Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this;
                             this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__);
                             return;
                         }
                     }
                     else
                     {
                         awaiter = this.<>u__1;
                         this.<>u__1 = default(TaskAwaiter<bool>);
                         this.<>1__state = -1;
                     }
                     this.<>s__3 = awaiter.GetResult();
                     this.<result>5__2 = this.<>s__3;
                     Console.WriteLine(this.<result>5__2);
                 }
                 catch (Exception exception)
                 {
                     this.<>1__state = -2;
                     this.<>t__builder.SetException(exception);
                     return;
                 }
                 this.<>1__state = -2;
                 this.<>t__builder.SetResult();
             }
             [DebuggerHidden]
             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
             {
             }
         }

接下来, 看GetInfoAsync()方法, 这个是自己编写的, 但是实现的细节,最终转换成了编译器执行代码:

        [AsyncStateMachine(typeof(Program.<GetInfoAsync>d__1)), DebuggerStepThrough]
        public static void GetInfoAsync()
        {
            Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = new Program.<GetInfoAsync>d__1();
            <GetInfoAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
            <GetInfoAsync>d__.<>1__state = -1;
            AsyncVoidMethodBuilder <>t__builder = <GetInfoAsync>d__.<>t__builder;
            <>t__builder.Start<Program.<GetInfoAsync>d__1>(ref <GetInfoAsync>d__); //注意到该代码, 调用了Start(),也许这就是默认实现的地方
        }

通过查看Start泛型方法的实现, 最终找到了, 该泛型的条件限制于必须实现与IAsyncStateMachine 接口, 所以通过查看, 该类最终调用了 MoveNext(), 而MoveNext中正

调用了GetAwaiter()。关于Start的实现如下所示:

[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
    if (stateMachine == null)
    {
        throw new ArgumentNullException("stateMachine");
    }
    ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
    RuntimeHelpers.PrepareConstrainedRegions();
    try
    {
        ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
        stateMachine.MoveNext();
    }
    finally
    {
        executionContextSwitcher.Undo();
    }
}

剖析MoveNext

对比IDE中的代码, 如下所示:

总结

await等待的是任务的操作值, 最终返回是异步操作的返回结果。而这一切都是因为编译器创建了一系列复杂的状态机制, 以达到其实现。

到此这篇关于C#异步编程async/await用法详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#实现异步编程的方法

    最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很有必要好好地学习一下C#异步编程. 什么是异步 异步指的就是不用阻塞当前线程来等待任务的完成,而是将任务扔到线程池中去执行,当前线程可以继续向下执行,直至其它线程将任务完成,并回调通知当前线程.整个任务从开始到结束都是异步完成的,不会阻塞当前线程.因此,异步很重要的一点就是,不会阻塞当前线程. 实现异步编程 在C#语言中,主要是通过委托来实现异步编程的,在委托类型中定义了两个方法BeginInvoke()和EndInvok

  • c# 异步编程入门

    一.什么算异步?   广义来讲,两个工作流能同时进行就算异步,例如,CPU与外设之间的工作流就是异步的.在面向服务的系统中,各个子系统之间通信一般都是异步的,例如,订单系统与支付系统之间的通信是异步的,又如,在现实生活中,你去馆子吃饭,工作流是这样的,点菜->下单->做你的事->上菜->吃饭,这个也是异步的,具体来讲你和厨师之间是异步的,异步是如此重要,因外它代表者高效率(两者或两者以上的工作可以同时进行),但复杂,同步的世界简单,但效率极极低. 二.在编程中的异步   在编程中,

  • C#异步编程几点需要注意的地方

    尽量不要编写返回值类型为void的异步方法 在通常情况下,建议大家不要编写那种返回值类型为void的异步方法,因为这样做会破坏该方法的启动者与方法本身之间的约定,这套约定本来可以确保主调方能够捕获到异步方法所发生的异常. 正常的异步方法是通过它返回的Task对象来汇报异常的.如果执行过程中发生了异常,那么Task对象就进入了faulted(故障)状态.主调方在对异步方法所返回的Task对象做await操作时,该对象若已处在faulted状态,系统则会将执行异步方法的过程中所发生的异常抛出,反之,

  • C#异步编程详解

    前言 本节主要介绍异步编程中Task.Async和Await的基础知识. 什么是异步? 异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程. 异步和多线程 相同点:避免调用线程阻塞,从而提高软件的可响应性. 不同点: 异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能.C#5.0 .NET4.5 以后关键字Async和Awai

  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    0.线程的本质 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度. 1.多线程: 使用多个处理句柄同时对多个任务进行控制处理的一种技术.据博主的理解,多线程就是该应用的主线程任命其他多个线程去协助它完成需要的功能,并且主线程和协助线程是完全独立进行的.不知道这样说好不好理解,后面慢慢在使用中会有更加详细的讲解. 2.多线程的使用: (1)最简单.最原始的使用方法:Thread oGetArgThre

  • C#编程总结(六)详解异步编程

    1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程计算机或查询数据库.异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行. 2.同步与异步的区别 同步(Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行. 异步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行.实质:异步操作,启动了新的线程,主线程与方法线程并行执行. 3.异

  • c# 异步编程基础讲解

    现代应用程序广泛使用文件和网络 I/O.I/O 相关 API 传统上默认是阻塞的,导致用户体验和硬件利用率不佳,此类问题的学习和编码的难度也较大.而今基于 Task 的异步 API 和语言级异步编程模式颠覆了传统模式,使得异步编程非常简单,几乎没有新的概念需要学习. 异步代码有如下特点: 在等待 I/O 请求返回的过程中,通过让出线程来处理更多的服务器请求. 通过在等待 I/O 请求时让出线程进行 UI 交互,并将长期运行的工作过渡到其他 CPU,使用户界面的响应性更强. 许多较新的 .NET

  • 深入分析C#异步编程详解

    同步方法和异步方法的区别同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作异步编程概览.NET Framework 允许您异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法.BeginInvoke 方法用于启动异步调用.它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)

  • c# 使用异步编程的方法

    怎么使用异步,就是用委托进行处理,如果委托对象在调用列表中只有一个方法,它就可以异步执行这个方法.委托类有两个方法,叫做BeginInvoke和EndInvoke,它们是用来异步执行使用. 异步有三种模式 等待模式,在发起了异步方法以及做了一些其它处理之后,原始线程就中断,并且等待异步方法完成之后再继续. 轮询模式,原始线程定期检查发起的线程是否完成,如果没有则可以继续做一些其它的事情. 回调模式,原始线程一直在执行,无需等待或检查发起的线程是否完成.在发起的线程中的引用方法完成之后,发起的线程

  • C#异步编程async/await用法详解

    异步函数简介 一般指 async 修饰符声明得.可包含await表达式得方法或匿名函数. 声明方式 异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字.async可以出现在返回值之前的任何位置, 如下示例: async public static void GetInfoAsync() { //... } public async static void GetInfoAsync() { //... } public static async void GetInfoAsy

  • Javascript异步编程async实现过程详解

    async官方DOC 介绍 node安装 npm install async --save 使用 var async = require('async') js文件 https://github.com/caolan/async/tree/master/dist async提供了很多函数用于异步流程控制,下面是async核心的几个函数,完整的函数请看async官方DOC async.map(['file1','file2','file3'], fs.stat, function(err, res

  • C#多线程系列之async和await用法详解

    目录 async和await async await 从以往知识推导 创建异步任务 创建异步任务并返回Task 异步改同步 说说awaitTask 说说 asyncTask<TResult> 同步异步? Task封装异步任务 关于跳到await变异步 为什么出现一层层的await async和await async 微软文档:使用 async 修饰符可将方法.lambda 表达式或匿名方法指定为异步. 使用 async 修饰的方法,称为异步方法. 例如: 为了命名规范,使用 async 修饰的

  • Java异步编程工具Twitter Future详解

    目录 异步编程(Twitter Future) 为啥要异步 基本用法 1.封装计算逻辑,异步返回. 2.异步计算结果串联异步处理 3.并行多个异步任务,统一等待结果 4.异步错误处理 Twitter包装 pom依赖 1.封装计算逻辑,异步返回 2.异步计算结果串联异步处理 3.并行多个异步任务 4.错误处理 其他用法 其他工具 异步编程(Twitter Future) 为啥要异步 异步编程有点难以理解,这东西感觉不符合常理,因为我们思考都是按照串行的逻辑,事都是一件一件办.但在异步计算的情况下,

  • JavaScript async/await使用详解

    目录 一.简介 二.async 三.await 四.案例 附-直接量/字面量 一.简介 async/await是ES20717引入的,主要是简化Promise调用操作,实现了以异步操作像同步的方式去执行,async外部是异步执行的,同步是await的作用. 二.async async,英文意思是异步,当函数(包括函数语句.函数表达式.Lambda表达式)前有async关键字的时候,并且该函数有返回值,函数执行成功,那么该函数就会调用Promise.resove()并隐式的返回一个Promise对

  • java异步编程CompletableFuture使用示例详解

    目录 一.简单介绍 二.常见操作 1.使用默认线程池 2.使用自定义线程池 3.获取线程的执行结果 三.处理异步结算的结果 四.异常处理 五.组合 CompletableFuture 六.并行运行多个 CompletableFuture 七.案例 1.从多个平台获取书价格 2.从任意一个平台获取结果就返回 一.简单介绍 CompletableFuture 同时实现了 Future 和 CompletionStage 接口. public class CompletableFuture<T> i

  • C#异步编程由浅入深(三)之详解Awaiter

      上一篇末尾提到了Awaiter这个类型,上一篇说了,能await的对象,必须包含GetAwaiter()方法,不清楚的朋友可以看上篇文章.那么,Awaiter到底有什么特别之处呢?  首先,从上篇文章我们知道,一个Awaiter必须实现INotifyCompletion接口,这个接口定义如下: namespace System.Runtime.CompilerServices { /// <summary> /// Represents an operation that will sch

  • Node.js如何对SQLite的async/await封装详解

    前言 本文主要给大家介绍的是关于Node.js对SQLite的async/await封装的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 用于将每个SQLite函数同步化,并可以用await的接口. 注意:需要SQLite for Node模块和Node.js 8.0+,并支持async / await. SQLite最常用作本地或移动应用程序的存储单元,当需要从程序的各个部分访问数据时,回调不是最佳解决方案. 为了在程序程序中更自然地访问数据,我编写了一个将回调转换为

  • Android编程之光线传感器用法详解

    本文实例讲述了Android编程之光线传感器用法.分享给大家供大家参考,具体如下: Android手机自带光线传感器,通常我们手机的屏幕自动亮度都是用光线传感器来实现的.该传感器在前置摄像头附近,此外,还有一个距离传感器.这里主要讲解如何使用Android手机的光线传感器. 获得感应器服务 Android开发中要使用光线传感器,需要先获得系统传感器服务Context.SENSOR_SERVICE,获得方法如下: SensorManager senserManager = (SensorManag

  • 利用jQuery异步上传文件的插件用法详解

    现在想实现用ajax来上传文件的功能,但是却发现Jquery自带的ajax方法只能上传文件名,而不能上传文件:用form提交虽然能够上传文件,但是却要刷新页面...多方查找下找到了一个可用的jQuery插件,刚好可以满足异步上传文件的要求. 代码 jquery.form.js 用法 这个插件是基于表单提交的,我们只要正常的写一段提交文件的表单,如: <form id="myForm" action="comment.php" method="post

随机推荐