c#异步编程async/await用法详解-kb88凯时官网登录

来自:网络
时间:2023-01-01
阅读:
免费资源网 - https://freexyz.cn/

异步函数简介

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

声明方式

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

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

异步方法的返回类型

异步函数的返回类型只能为: void、task、task

task: 代表一个返回值t类型的操作。

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

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

await(等待)

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

        public async static void getinfoasync()
        {
            await getdata(); // 等待异步操作, 无返回值
            await getdata(1); //等待异步操作, 返回值 int
        }
        static task getdata()
        {
            //...
            return null;
        }
        static task getdata(int a)
        {
            //...
            return null;
        }

注: await 最终操作的是一个值, 当然, 也可以是无值, 如上getdata() , 否则就是一个 task 如上: getdata()

await执行过程

taskawaiter 获取执行结果

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

所以这个时候,taskawaiter出现了, 无论是task、还是task操作, 都具备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 task = task.run(() =>
            {
                thread.sleep(10000); //模拟耗时
                return true;
            });
            
            //以下两种方式
            bool taskresult1 = await task;  //内部自己执行了getawaiter() 
            bool taskresult = task.getawaiter().getresult();  //自己手动执行awaiter(), 但是阻塞ui
       console.writeline(taskresult);
        }

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

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

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

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

        [compilergenerated]
        [serializable]
        private sealed class <>c
        {
            public static readonly program.<>c <>9 = new program.<>c();
            public static func <>9__1_0;
            internal bool b__1_0()
            {
                thread.sleep(10000);
                return true;
            }
        }

第二个类d__1 分析:

该类分别实现了接口 iasyncstatemachine 的movenext() 与 setstatemachine() ,另外 注意,

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

 [compilergenerated]
         private sealed class d__1 : iasyncstatemachine
         {
             public int <>1__state;
             public asyncvoidmethodbuilder <>t__builder;
             private task 5__1;
             private bool 5__2;
             private bool <>s__3;
             private taskawaiter <>u__1;
             void iasyncstatemachine.movenext()
             {
                 int num = this.<>1__state;
                 try
                 {
                     taskawaiter awaiter;
                     if (num != 0)
                     {
                         func arg_2f_0;
                         if ((arg_2f_0 = program.<>c.<>9__1_0) == null)
                         {
                             arg_2f_0 = (program.<>c.<>9__1_0 = new func(program.<>c.<>9.b__1_0));
                         }
                         this.5__1 = task.run(arg_2f_0);
                         awaiter = this.5__1.getawaiter();
                         if (!awaiter.iscompleted)
                         {
                             this.<>1__state = 0;
                             this.<>u__1 = awaiter;
                             program.d__1 d__ = this;
                             this.<>t__builder.awaitunsafeoncompleted, program.d__1>(ref awaiter, ref d__);
                             return;
                         }
                     }
                     else
                     {
                         awaiter = this.<>u__1;
                         this.<>u__1 = default(taskawaiter);
                         this.<>1__state = -1;
                     }
                     this.<>s__3 = awaiter.getresult();
                     this.5__2 = this.<>s__3;
                     console.writeline(this.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.d__1)), debuggerstepthrough]
        public static void getinfoasync()
        {
            program.d__1 d__ = new program.d__1();
            d__.<>t__builder = asyncvoidmethodbuilder.create();
            d__.<>1__state = -1;
            asyncvoidmethodbuilder <>t__builder = d__.<>t__builder;
            <>t__builder.startd__1>(ref d__); //注意到该代码, 调用了start(),也许这就是默认实现的地方
        }

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

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

[securitysafecritical, debuggerstepthrough, __dynamicallyinvokable]
public void start(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用法详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图