感谢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;
}
}

说明

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方法,就创建几个【状态机】。

.【状态机】的几种状态(-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 = ; x < ; 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 -:
goto Label_01E1; case :
case :
break; default:
this.<local>5__a = this.argument;
break;
}
try
{
switch (this.<>1__state)
{
}
try
{
TaskAwaiter<Program.Type1> awaiter;
switch (this.<>1__state)
{
case :
break; case :
goto Label_0137; default:
awaiter = Program.Method1Async().GetAwaiter();
if (awaiter.IsCompleted)
{
goto Label_00D7;
}
this.<>1__state = ;
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 = -;
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 = ;
while (this.<x>5__c < )
{
TaskAwaiter<Program.Type2> awaiter3 = Program.Method2Async().GetAwaiter();
if (awaiter3.IsCompleted)
{
goto Label_0156;
}
this.<>1__state = ;
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 = -;
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 = -;
this.<>t__builder.SetException(exception);
return;
}
Label_01E1:
this.<>1__state = -;
this.<>t__builder.SetResult(str);
} [DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine param0)
{
this.<>t__builder.SetStateMachine(param0);
}
}

这里我要说一句抱歉,这里贴出的代码可读性很差(没有关键字,没有着色)。本来想截图的,但是由于反编译的代码很长,无法在我显示器中一屏显示完,所以我也没截图。下面讲解的时候,采用分段截图来说明,希望大家看得明白。同时也推荐大家自己用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[];
FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, , FileOptions.Asynchronous);
fs.BeginRead(s_data, , s_data.Length, ReadIsDone, fs); Console.WriteLine("主线程执行完毕"); Console.ReadLine();
} private static void ReadIsDone(IAsyncResult ar)
{
Thread.Sleep();
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); }

这种编程模式有一个很大的缺点:如果我想【获取异步返回的结果,然后打印出来】。那么【获取异步返回结果,然后打印出来】必须放在回调函数中,也就是另一块代码中(上面的实例,获取字节数,然后打印出来。就必须放在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[];
FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, , FileOptions.Asynchronous);
return await Task.Factory.FromAsync<byte[], int, int, int>(fs.BeginRead, fs.EndRead, s_data, , 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;
}

感谢大家的耐心阅读

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

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

    序 最近看了一些园友们写的有关于异步的文章,受益匪浅,写这篇文章的目的是想把自己之前看到的文章做一个总结,同时也希望通过更加通俗易懂的语言让大家了解"异步"编程. 1:什么是异步 ...

  2. .Net Core异步async/await探索

    走进.NetCore的异步编程 - 探索 async/await 前言: 这段时间开始用.netcore做公司项目,发现前辈搭的框架通篇运用了异步编程方式,也就是async/await方式,作为一个刚 ...

  3. Python 进阶 异步async/await

    一,前言 本文将会讲述Python 3.5之后出现的async/await的使用方法,我从上看到一篇不错的博客,自己对其进行了梳理.该文章原地址https://www.cnblogs.com/dhcn ...

  4. 异步 async & await

    1 什么是异步 异步的另外一种含义是计算机多线程的异步处理.与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程. 2 异步场景 l  不 ...

  5. C#Framework4.0支持异步async/await语法

    由于用户使用的是XP系统,但是程序里异步都是通过async/await代码来实现的,然而async/await需要Framework4.5版本才可以,而XP系统最高只能支持到Framework4.0, ...

  6. 请教 C# 异步 async await 问题

    各位园友,之前对C#异步只是肤浅了解,请教一个具体问题. 需求: 前台会发送一个Array,这个数组都是 id的集合,比较大.分两步,首先保存这些id,然后去调用异步方法. 可以正常返回json,也可 ...

  7. async/await简单使用

    function process(i) { var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.l ...

  8. .Net异步关键字async/await的最终理解

    由于之前的项目中自己突然想试试异步action,于是使用了一下,突然就对异步action的执行流程以及原理及其好处产生了兴趣,再参考了一些文章之后,就做了下归类. 我们可以不需要太深入的理解底层,但是 ...

  9. 【学习笔记】深入理解async/await

    参考资料:理解javaScript中的async/await,感谢原文作者的总结,本文在理解的基础上做了一点小小的修改,主要为了加深自己的知识点掌握 学完了Promise,我们知道可以用then链来解 ...

随机推荐

  1. vue 里filter的基本用法

    filter是和data  computed   methods watch一样,都是new Vue()的参数. 用于对简单数据的处理,和computed有冲突,所以从vue2.0后就对filter做 ...

  2. Jupyter notebook安装

    之前就装了jupyter notebook,但今天打开来发现是python2,并且似乎没法转换到python3??? so,再把python3的版本安装一下 打开CMD pip install jup ...

  3. 根据Excel模板,填写报表,并下载到web浏览器端

    package com.neusoft.nda.basic.recordmanager.viewelec.servlet; import java.io.File; import java.io.Fi ...

  4. ECMAScript基础

    概念: 1):区分大小写 2):变量是弱类型的. 3):每行结尾的分号可有可无 4):注释与Java,C和PHP语言的注释相同 5):括号表明代码块 原始值:是存储在栈中的简单数据段,也就是说他们的值 ...

  5. Golang数据类型总结及其转换

    golang数据类型 基本类型:boolean,numeric,string类型的命名实例是预先声明的. 复合类型:array,struct,指针,function,interface,slice,m ...

  6. hash 位运算 练习

    hash  位运算 [以下代码仅做位运算的练习,算法本身不合理  php转译python] 从头到尾彻底解析Hash表算法_知识库_博客园 https://kb.cnblogs.com/page/18 ...

  7. 解决秒杀活动高并发出现负库存(Redis)

    商城在秒杀活动开始时,同时有好多人来请求这个接口,即便做了判断库存逻辑,也难免防止库存出现超卖,造成损失 Django中的ORM本身就对数据库做了防范,但再过亿级访问也扛不住 下面利用Redis的过载 ...

  8. HTML轮播图实现(前后端分离)

    1,首先前后端分离用到了3个插件 2,异步请求后端获取数据库图片地址(图片名字) //图片轮播 axios({ url:'http://127.0.0.1:8000/userctrl/image', ...

  9. 深度学习基础(一)LeNet_Gradient-Based Learning Applied to Document Recognition

    作者:Yann LeCun,Leon Botton, Yoshua Bengio,and Patrick Haffner 这篇论文内容较多,这里只对部分内容进行记录: 以下是对论文原文的翻译: 在传统 ...

  10. Django进阶之QuerySet和中介模型

    QuerySet QuerySet是查询集,就是传到服务器上的url里面的查询内容.其形态类似于Python的列表,列表中的元素是QuerySet对象.支持大部分列表的内置方法. 可切片 QueryS ...