异步async/await简单应用与探究

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

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

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

示例代码

    class Program
    {
        static void Main(string[] args)
        {
            EasyAsyncAwait();

            Console.ReadLine();
        }

        static async void EasyAsyncAwait()
        {
            //①
            Task<string> task = GetName();

            //③
            Console.WriteLine("DoSomeThing");

            //⑤
            Console.WriteLine("Result:{0}", await task);
        }

        //②
        private static async Task<string> GetName()
        {
            //④
            return await Task.Run(() =>
            {
                Thread.Sleep(5000);
                return "Test";
            });
        }
    }

说明

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

2.代码的执行顺序已经标注,主要关注②、③。②执行后代码立刻返回执行③,然后当⑤需要访问结果时,等待④执行完毕返回结果。

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

4.当代码运行到async,程序会判断异步方法是否执行完毕。如果执行完毕,获取结果,代码继续往下运行。如果未执行完毕,线程返回(可以处理系统安排的别的任务),记录下当前位置(以便异步方法执行完毕后,合适的线程继续调用未执行的代码),代码不继续运行直到异步函数返回结果。(这点绝对是重中之重,理解了这点,会解决心中对async/await的疑惑)

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

async/await实现异步原理:当执行代码遇到async时,知道该方法为异步方法,然后开始检查该方法体内一共有多少个await。如果await的个数>0,那么就创建状态机,来维护异步方法线程的返回与异步方法执行完毕之后的结果调用。(以上是我个人的理解,如有错误请各位指正,谢谢)

通过上面的介绍,我们知道状态机在异步方法线程的返回与结果的调用中起着至关重要的作用,下面我们就来对状态机一探究竟。

下面这段测试代码,来自: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 { }

用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);
    }
}

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

代码阅读

1.字段

这里我们最要关注的字段就是:int <>1__state(初始状态-1),这个字段控制着异步方法当前的状态(未执行、执行完毕),并且负责跳转到对应的异步方法(如果有多个await,就由它负责切换执行对应的代码)

2.核心代码

这段代码的大致意思是:<>1__state初始状态为-1,进入default分支,判断Method1Async()是否执行完毕,若未执行完毕,<>1__state设计为0(表示执行完毕),调用了一个方法(红框标注),然后return(线程返回)。

下面重点关注一下,红框标注的代码(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);

        }

异步函数简化代码

        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);
        }

异步函数(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;
        }    

感谢大家的耐心阅读

时间: 2024-09-30 20:35:50

异步async/await简单应用与探究的相关文章

我也来说说C#中的异步:async/await

序 最近看了一些园友们写的有关于异步的文章,受益匪浅,写这篇文章的目的是想把自己之前看到的文章做一个总结,同时也希望通过更加通俗易懂的语言让大家了解"异步"编程. 1:什么是异步 应用程序在启动后,会产生一个进程,进程是构成应用程序资源的集合.在进程内部有称之为线程的对象,线程才是真正负责执行运行命令的. 线程: 1:默认情况下,一个应用程序只会有一个线程,从程序开始到结束. 2:一个进程如果有多个线程,那么多个线程将会共享进程内的资源. 3:线程不是派生于不同的线程. 一般来说,我们

.net4.5的异步 async await 和Task

首先应该明白Task的概念-----任务 //这是一种优于Thread,甚至是ThreadPool的东西 而且是默认异步 再来理解async和await: 1.async只是配合await,用来声明异步方法的关键字: 2.async和await之间,await才是关键,直接含义——"异步等待",主线程运行至异步方法内的await处,一般情况下主线程会立即返回上层方法,而await处后续的任务(方法)会新开线程继续执行,新开线程在执行“等待”出任务(方法)结果后,会接着运行完异步方法内a

异步async await 相关知识点总结以及代码练习

<script> const setTimeoutToPromise = duration => new Promise(resolve => { setTimeout(resolve, duration) }) export default { data() { return { mysrc: true } }, methods: { s1(a){ console.log(a) }, s2(a){ console.log(a) }, s3(a){ console.log(a) }

asp.net 异步(async/await)中访问HttpContext的问题

以web api上传文件的官方例子为例: await Request.Content.ReadAsMultipartAsync(provider); 项目里面多处用到session,包括在其他类库中通过HttpContext获取Session对象,在await之后,直接访问Session均为空了. 怎么办,文件得上传啊. 网络搜索无果,后来发现HttpContext竟然支持Set方法,那么在await之前存储HttpContext的引用,在await之后将引用再赋给HttpContext,这样似

ASP.NET WebForm中用async/await实现异步出人意料的简单

1. 在.aspx中添加异步标记 <%@ Page Language="C#" Async="true"%> 2. 在.aspx.cs或者.ascx.cs(用户控件)中添加异步方法 private async Task GetMyPosts() { var posts = await ServiceFactory.BlogPostSevice.GetBlogPostsPagedAsync(); rpPosts.DataSource = posts; rp

【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 二.同步代码存在的问题 对于同步的代码,大家肯定都不陌生,因为我们平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题,例如我们向一个Web服务器发出一个

C#Async,await异步简单介绍

C# 5.0 引入了async/await,.net framework4.5开始支持该用法 使用: 由async标识的方法必须带有await,如果不带await,方法将被同步执行 static void Main(string[] args) { fun22(); Console.WriteLine($"Main await ThreadId {Thread.CurrentThread.ManagedThreadId}"); Console.ReadKey(); } private

Async Await异步调用WebApi

先铺垫一些基础知识 在 .net 4.5中出现了 Async Await关键字,配合之前版本的Task 来使得开发异步程序更为简单易控. 在使用它们之前 我们先关心下 为什么要使用它们.好比 一个人做几件事,那他得一件一件的做完,而如果添加几个人手一起帮着做 很显然任务会更快的做好.这就是并行的粗浅含义. 在程序中,常见的性能瓶颈在于 NetWork I/O 瓶颈 , CPU 瓶颈, 数据库I/O瓶颈,这些瓶颈使得我们的程序运行的很慢,我们想办法去优化.因为并行开发本身就加重CPU负担,所以一般

.NET 中的 async/await 异步编程

前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在阅读的过程中发现不对的地方,欢迎大家指正. 同步编程与异步编程 通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行.而异步编程的核心是使用多线程,通过让不同的线程执行不同的任务,实现不同代码的并行运行. 前台线程与后台线程 关于多线程,早在.NET2