zoukankan      html  css  js  c++  java
  • 异步async/await简单应用与探究

    感谢Marco CAO指出的两点错误,已做出修改与补充

    异步函数(async/await)简单应用

    .NET Framework4.5提供了针对异步函数语法糖,简化了编写异步函数的复杂度。

    下面通过一个简单的示例,介绍.NET Framework4.5对异步函数的支持。

    窗体页面

    窗体代码

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private async void btnGetText_Click(object sender, EventArgs e)
            {
                string urlString = string.Empty;
                using (HttpClient client = new HttpClient())
                {
                    //异步获取“http://10.32.112.82/epms/”内容(线程返回)。用户界面依旧可以操作
                    urlString = await client.GetStringAsync(@"http://10.32.112.82/epms/");
                }
                txtUrlString.Text = urlString;
            }
        }
    View Code

    说明

    1.点击获取url,程序开始获取url内容。(注意:这里我写的url是我们公司内部的网址,必须通过代理才可以访问,所以肯定会有延迟效果。

    2.虽然有延迟效果,但是窗体依旧可以操作(在第一个textBox中可以输入内容)。表明窗体并没有被阻塞,也就是说获取url内容是异步操作。

    3.一段时间后,你会发现窗体报错,表明获取url内容操作完毕,操作失败。

    讲解

    1.异步函数的关键字:async;等待异步结果的关键字:await

    2.异步方法的调用,与获取异步方法的结果,在同一个方法体内。这点极大了简化异步方法维护的成本。(以前BeginInvoke()、EndInvoke(),起码是2个方法,且不能在同一方法体内调用。下文异步函数简化APM模型有介绍。)

    3.await作用的方法表示:【进入该方法的线程】会返回线程池,程序会在此挂起等待返回结果(这里需要注意,因为程序得到返回结果再执行时,是从线程池中另起了一个线程在执行,同时【子线程】会执行await后面的方法(这点是:async与await的核心)

    异步函数(async/await)实现异步原理(状态机)

    async/await关键点:

    1.有几个async方法,就创建几个【状态机】。

    2.状态机】的几种状态(-1:初始化;-2:方法体执行结束;0:第一个await方法;1:第二个await方法;。。。依次往下

    3.【状态机】控制着异步方法执行流程(MoveNext方法

    通过上面的介绍,我们知道【状态机控制着方法执行流程,下面我们就来对【状态机】如何控制方法执行流程一探究竟。

    下面这段测试代码,来自:CLR via C#(第4版)

    示例代码

            private static async Task<string> MyMethodAsync(int argument)
            {
                int local = argument;
                try
                {
                    Type1 result1 = await Method1Async();
    
                    for (int x = 0; x < 3; x++)
                    {
                        Type2 result2 = await Method2Async();
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("Catch");
                }
                finally
                {
                    Console.WriteLine("Finally");
                }
                return "Done";
            }
    
            private static async Task<Type1> Method1Async()
            {
                string a = await Task.Run<string>(() => "Method1");
                return new Type1();
            }
    
            private static async Task<Type2> Method2Async()
            {
                string a = await Task.Run<string>(() => "Method2");
                return new Type2();
            }
    
            sealed class Type1 { }
            sealed class Type2 { }
    View Code

    用Reflector.exe反编译后,得到如下代码(MyMethodAsync)

    [CompilerGenerated]
    private struct <MyMethodAsync>d__9 : IAsyncStateMachine
    {
        // Fields
        public int <>1__state;
        public AsyncTaskMethodBuilder<string> <>t__builder;
        private object <>t__stack;
        private TaskAwaiter<Program.Type1> <>u__$awaitere;
        private TaskAwaiter<Program.Type2> <>u__$awaiterf;
        public int <local>5__a;
        public Program.Type1 <result1>5__b;
        public Program.Type2 <result2>5__d;
        public int <x>5__c;
        public int argument;
    
        // Methods
        private void MoveNext()
        {
            string str;
            try
            {
                bool flag = true;
                switch (this.<>1__state)
                {
                    case -3:
                        goto Label_01E1;
    
                    case 0:
                    case 1:
                        break;
    
                    default:
                        this.<local>5__a = this.argument;
                        break;
                }
                try
                {
                    switch (this.<>1__state)
                    {
                    }
                    try
                    {
                        TaskAwaiter<Program.Type1> awaiter;
                        switch (this.<>1__state)
                        {
                            case 0:
                                break;
    
                            case 1:
                                goto Label_0137;
    
                            default:
                                awaiter = Program.Method1Async().GetAwaiter();
                                if (awaiter.IsCompleted)
                                {
                                    goto Label_00D7;
                                }
                                this.<>1__state = 0;
                                this.<>u__$awaitere = awaiter;
                                this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type1>, Program.<MyMethodAsync>d__9>(ref awaiter, ref this);
                                flag = false;
                                return;
                        }
                        awaiter = this.<>u__$awaitere;
                        this.<>u__$awaitere = new TaskAwaiter<Program.Type1>();
                        this.<>1__state = -1;
                    Label_00D7:
                        Program.Type1 introduced11 = awaiter.GetResult();
                        awaiter = new TaskAwaiter<Program.Type1>();
                        Program.Type1 type = introduced11;
                        this.<result1>5__b = type;
                        this.<x>5__c = 0;
                        while (this.<x>5__c < 3)
                        {
                            TaskAwaiter<Program.Type2> awaiter3 = Program.Method2Async().GetAwaiter();
                            if (awaiter3.IsCompleted)
                            {
                                goto Label_0156;
                            }
                            this.<>1__state = 1;
                            this.<>u__$awaiterf = awaiter3;
                            this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type2>, Program.<MyMethodAsync>d__9>(ref awaiter3, ref this);
                            flag = false;
                            return;
                        Label_0137:
                            awaiter3 = this.<>u__$awaiterf;
                            this.<>u__$awaiterf = new TaskAwaiter<Program.Type2>();
                            this.<>1__state = -1;
                        Label_0156:
                            Program.Type2 introduced12 = awaiter3.GetResult();
                            awaiter3 = new TaskAwaiter<Program.Type2>();
                            Program.Type2 type2 = introduced12;
                            this.<result2>5__d = type2;
                            this.<x>5__c++;
                        }
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Catch");
                    }
                }
                finally
                {
                    if (flag)
                    {
                        Console.WriteLine("Finally");
                    }
                }
                str = "Done";
            }
            catch (Exception exception)
            {
                this.<>1__state = -2;
                this.<>t__builder.SetException(exception);
                return;
            }
        Label_01E1:
            this.<>1__state = -2;
            this.<>t__builder.SetResult(str);
        }
    
        [DebuggerHidden]
        private void SetStateMachine(IAsyncStateMachine param0)
        {
            this.<>t__builder.SetStateMachine(param0);
        }
    }
    
     
    View Code

    这里我要说一句抱歉,这里贴出的代码可读性很差(没有关键字,没有着色)。本来想截图的,但是由于反编译的代码很长,无法在我显示器中一屏显示完,所以我也没截图。下面讲解的时候,采用分段截图来说明,希望大家看得明白。同时也推荐大家自己用Reflector.exe反编译后,自己阅读。

    代码阅读

    1.【状态机】初始化(-1)

    2.【状态机】Start入口-》MoveNext方法

    3.【状态机】个数(3个async方法,所以3个状态机)

    4.核心代码(MoveNext方法)

    这段代码的大致意思是:<>1__state初始状态为-1,进入default分支,判断Method1Async()是否执行完毕,若未执行完毕,<>1__state设计为0(表示返回第一个await对应的方法块),调用了一个方法(红框标注),然后return(线程返回线程池,函数挂起等待返回结果)。若执行完毕,【goto Label_00D7】正好就是获取第一个await结果的代码块。

    下面重点关注一下,红框标注的代码(AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted())做了什么?

    依旧需要借助反编码工具,查到这个调用的真实代码,如下

    第一句是关键,方法名称:得到完成时调用的委托。进入方法一探究竟

    AsyncTaskMethodBuilder.GetCompletionAction()代码如下

    红框的批注,action就是完成时调用的方法,进入要MoveNextRunner.Run()方法中。

    真相大白,action就是状态机的MoveNext()方法。

    总结:AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted()做得事就是,当异步函数执行完毕后,回调状态机的MoveNext()方法

    异步函数(async/await)简化异步编程模型(APM)

    APM编程代码

            static void Main(string[] args)
            {
                AsyncAPM();
            }
    
            static void AsyncAPM()
            {
                Console.WriteLine("Main thread ID={0}", Thread.CurrentThread.ManagedThreadId);
    
                byte[] s_data = new byte[100];
                FileStream fs = new FileStream(@"d:1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
                fs.BeginRead(s_data, 0, s_data.Length, ReadIsDone, fs);
    
                Console.WriteLine("主线程执行完毕");
    
                Console.ReadLine();
            }
    
            private static void ReadIsDone(IAsyncResult ar)
            {
                Thread.Sleep(5000);
                Console.WriteLine("ReadIsDone thread ID={0}", Thread.CurrentThread.ManagedThreadId);
    
                FileStream fs = (FileStream)ar.AsyncState;
                int bytesRead = fs.EndRead(ar);
                fs.Close();
    
                Console.WriteLine("Number of bytes read={0}", bytesRead);
    
            }
    View Code

    这种编程模式有一个很大的缺点:如果我想【获取异步返回的结果,然后打印出来】。那么【获取异步返回结果,然后打印出来】必须放在回调函数中,也就是另一块代码中(上面的实例,获取字节数,然后打印出来。就必须放在ReadIsDone函数中),这其实给阅读代码增添了难度(如果多个异步函数,阅读代码时就要在很多方法中来回跳转)。当然你也可以在一个方法中用EndInvoke()来做等待。这样虽然也是异步,但是会出现主程序阻塞,等待异步返回结果。

    异步函数简化代码

            static void Main(string[] args)
            {
                SimplifyAsyncToAPM();
                Console.ReadLine();
            }
    
            static async void SimplifyAsyncToAPM()
            {
                int length = await AsyncToAPM();
                Console.WriteLine("File length={0}", length);
            }
    
            static async Task<int> AsyncToAPM()
            {
                byte[] s_data = new byte[100];
                FileStream fs = new FileStream(@"d:1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
                return await Task.Factory.FromAsync<byte[], int, int, int>(fs.BeginRead, fs.EndRead, s_data, 0, s_data.Length, fs);
            }
    View Code

    异步函数(async/await)应用于事件编程模型

    示例代码(读取网络中一张图片的字节数)

            static void Main(string[] args)
            {
                Task<int> result = AsyncEvent(new Uri("http://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&pn=0&spn=0&di=84991311930&pi=&rn=1&tn=baiduimagedetail&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=3063552411%2C3030228420&os=1484689785%2C3968535026&simid=0%2C0&adpicid=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=11&oriquery=&objurl=http%3A%2F%2Fwww.52ij.com%2Fuploads%2Fallimg%2F160317%2F1110104P8-4.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bcdt3_z%26e3Bv54AzdH3F25g2xtw5AzdH3F9cmaa0_z%26e3Bip4s&gsm=0"));
                Console.WriteLine(result.Result);
            }
    
            static async Task<int> AsyncEvent(Uri uri)
            {
                WebClient wc = new WebClient();
                TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    
                wc.DownloadDataCompleted += (s, e) =>
                {
                    if (e.Cancelled)
                        tcs.SetCanceled();
                    else if (e.Error != null)
                        tcs.SetException(e.Error);
                    else
                        tcs.SetResult(e.Result.Count());
                };
    
                wc.DownloadDataAsync(uri);
    
                return await tcs.Task;
            }    
    View Code

    感谢大家的耐心阅读

  • 相关阅读:
    RESTful API 设计指南
    理解RESTful架构
    django-mysqlclient_1193错误
    获取当前脚本所在的目录和路径
    20191007
    20191005
    20191001
    20190927
    20190922
    莫比乌斯反演证明
  • 原文地址:https://www.cnblogs.com/color-wolf/p/5448458.html
Copyright © 2011-2022 走看看