Async and Await 异步和等待
【第一次这么耐下性子认真写博客,虽然觉得很认真了,当毕竟是第一次嘛,以后再看肯定觉得很不咋滴的,更何况园子里有那么多的高人和大侠,这篇文章就权当练练手了,熟悉一下用客户端发表博客了,也希望大家多多照顾新人,这厢有礼了!】下面正式开始,GO!
目录
Introducing the Keywords 介绍关键字 Awaitables 异步操作 Return Types 返回类型 Returning Values 返回值 Context 上下文 Avoiding Context:避免上下文 Async Composition 异步组合 Guidelines 指南 Next Steps 下一步首先,号外号外:异步将从根本上改变大多数编写代码的方式。是的,我相信异步/等待将会比Linq的影响更大。理解异步将会是未来几年的基本必须品。
Introducing the Keywords 介绍关键字
最简单的异步方法的样子就像下面这样:
public async TaskDoSomethingAsync()
{
awaitTask.Delay(100);//异步等待100ms
}
async关键字在这个方法中启用了await关键字,并且改变了方法处理结果的方式。这就是async关键字所做的!它没有在线程池的线程上运行这个方法,也没有做任何其他奇妙的事情。async关键字仅仅启用了await关键字(并且管理方法结果)。
异步方法从一开始就像其他任何方法一样执行下去,即,一开始同步运行直到遇到一个”await“(或者抛出一个异常)。
await关键字是可以获得异步的地方。await就像一个一元操作符:它只需要一个参数,一个awaitable(一个"awaitable"是一个异步操作)。await检查异步操作是否已经完成;如果异步操作已经完成了,那么这个方法就继续执行(就像一个普通的同步方法一样)。
如果await看到了异步操作没有完成,那么它就异步执行。它告诉这个异步操作当此异步操作本身完成的时候,运行这个方法的剩余部分,然后返回到该异步方法。
然后,当异步操作完成时,将执行异步方法的剩余部分。如果你正在等待一个内置的异步操作(比如一个任务),然后异步方法的剩余部分将会在”await“返回之前被捕捉到的”上下文“上执行。
我喜欢把”await“看做是”异步的await“。那就是说,在异步操作完成之前,异步方法一直是暂停的(因此它等待),但是实际的线程并没有阻塞(所以它是异步的)。
Awaitables 异步操作
.NET framework中有2种已经共用的异步操作类型: Task<T> 和Task。也有其他的异步操作类型:特殊的方法,如“Task.Yield”返回不是任务类型的异步操作。你也可以创建自己的异步操作(经常出于性能原因考虑),或者使用扩展方法来产生非异步操作类型的异步操作。
关于异步操作的很重要的一点是:这个类型是异步操作,此类型不是指方法返回的类型。换言之,你可以等待返回Task的异步方法的结果...因为这个方法返回Task,而不是因为它是异步的。所以你可以等待返回Task的非异步的方法的结果,如下:
public async Task NewStuffAsync()
{
// 使用await.
await ...
} public Task MyOldTaskParallelLibraryCode()
{
// 注意该方法不是一个异步方法,不能在该方法体内使用await
...
} public async Task ComposeAsync()
{
// 我们可以 await Tasks,不管它们来自哪里.
await NewStuffAsync();
await MyOldTaskParallelLibraryCode();
}
提示:如果你有一个简单的异步的方法,你可以不使用await关键字来实现它(如使用Task.FromResult)。如果你可以不使用await,那就不要使用,并且移除async关键字。一个返回Task.FromResult的非异步方法比返回一个值的异步方法更高效。
Return Types 返回类型
异步方法可以返回Task,Task<T>或者void。在几乎所有情况下,有可以返回Task或Task<T>,只有必要时才返回void。
为啥返回Task或者Task<T>呢?因为他们是异步操作,而void不是。所以如果你有一个返回Task或者Task<T>的方法,那么你可以把结果传递给await。对于返回值为void的方法,你没有东西传给await。当你有同步事件句柄的时候,必须返回void。
对于其他一些高级的操作你可以使用async void,比如一个单独的“static async void MainAsync()”控制台程序。然而,async void的使用有它自己的问题,详见异步控制台程序。async void方法最关键的用例是事件句柄。
Returning Values 返回值
异步方法返回类型为Task或者void,表示没有返回值,返回一个Task<T>表示必须返回一个T类型的值。
public async Task<int> CalculateAnswer()
{
await Task.Delay(100); // (Probably should be longer...) // Return "int"类型, 不是"Task<int>"
return 42;
}
虽然习惯起来有一点别扭,但这有一些背后设计的原因(异步CTP(Async CTP)为什么那样工作?
Context 上下文
当等待一个内置的异步操作时,内置的异步操作将会捕获当前的上下文context,然后会把它应用到这个异步方法的剩余部分。
那什么是context呢?
简单理解如下:
1.如果你正处于一个UI线程,那么它就是一个UI上下文。
2.如果你正在响应一个ASP.NET请求,那它就是一个ASP.NET请求上下文。
3.否则,它通常是一个线程池上下文。
复杂理解如下:
1.如果SynchronizationContext.Current不是null,那么它就是当前的SynchronizationContext.Current。(UI 和ASP.NET请求上下文都是SynchronizationContext 对象)
2.否则,它就是当前的TaskScheduler(TaskScheduler.Default是线程池上下文)。
在真实世界中,这意味着什么呢?首先,捕获(和存储)UI/ASP.NET上下文是透明的,即不可见的。
// WinForms 例子(对于 WPF同样有效).
private async void DownloadFileButton_Click(object sender,EventArgs e)
{
// 当我们异步等待的时候,UI线程没有被文件下载阻塞。
await DownloadFileAsync(fileNameTextBox.Text);// 因为我们还处于UI线程上,所以还可以直接访问UI元素。
resultTextBox.Text="File downloaded!";
}
// ASP.NET 例子
protected async void MyButton_Click(object sender,EventArgs e)
{
// 当我们异步等待的时候,ASP.NET线程没有被文件下载阻塞。
// 当我们等待的时候,就可以使用当前线程处理其他的请求。
await DownloadFileAsync(...);// 既然我们还在ASP.NET上下文上,我们就可以访问当前请求。
//虽然实际上我们可能在其他的线程上,但是我们仍有相同的ASP.NET请求上下文。
Response.Write("File downloaded!");
}
Avoiding Context:避免上下文
多数时候,你不需要同步返回到main上下文。记住:大多数异步方法被组合设计--他们等待其他多个操作,每一个操作本身代表一个异步操作(每个异步操作又可以被多个异步操作组合)。
这种情况下,你想要告诉异步者(awaiter)通过调用ConfigureAwait并且传递false不要捕捉当前上下文,比如:
private async TaskDownloadFileAsync(string fileName)
{
// 使用HttpClient或其他东西来下载文件内容。
var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false);// 注意因为 ConfigureAwait(false),此时我们就不在原始的上下文了.
// 取而代之的是,我们正运行在线程池上.
// 将文件内容写入磁盘文件.
await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false);
}
// WinForms和 WPF均适用.这次没有调用ConfigureAwait(false)
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// 当我们异步等待的时候,ASP.NET线程没有被文件下载阻塞。
await DownloadFileAsync(fileNameTextBox.Text);// 因为我们还处于UI线程上,所以还可以直接访问UI元素。
resultTextBox.Text = "File downloaded!";
}
这个例子最重要的值得注意的是每一级的异步方法的调用都有自己的上下文。DownloadFileButton_Click开始是在UI线程上,然后调用了DownloadFileContentAsync,DownloadFileContentAsync一开始也在这个UI线程上。然后通过调用ConfigureAwait(false)跳出当前上下文。DownloadFileContentAsync剩余部分就运行在线程池上下文中了。然而,当DownloadFileContentAsync执行完成后,当DownloadFileButton_Click 恢复时,它(DownloadFileButton_Click )再次恢复到当前UI线程中了。
好的经验是:除非你知道你确实需要这个上下文,你可以使用ConfigureAwait(false)。
Async Composition 异步组合
至此,我们仅仅考虑了序列组合:一个异步方法一次等待一个操作。开始多个操作并且等待这些操作的一个(或全部)完成的情况也是可能的。
可以通过开始这些操作而不等待等待它们,直到以后再等待它们来达到这个目的。
public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = DoOperation2Async(); //此时,所有的3个任务在同一时间运行。 // 现在,我们等待它们。
await Task.WhenAll(tasks);
} public async Task<int> GetFirstToRespondAsync()
{
// 调用2个web服务; 获取第一个响应.
Task<int>[] tasks = new[] { WebService1Async(), WebService2Async() }; // 等待第一个任务响应.
Task<int> firstTask = await Task.WhenAny(tasks); // Return结果.
return await firstTask;
}
通过使用并发组合 (Task.WhenAll 或Task.WhenAny),可以执行简单的并发操作。也可以伴随Task.Run来使用这些方法来做一些简单的并行计算。
然而,这不是任务并行库(TPL)的替代品,任何高级的CPU集中的并行操作应该使用TPL来完成。
Guidelines 指南
有兴趣的朋友可以读一下这篇文章 Task-based Asynchronous Pattern (TAP) document(链接地址:
http://www.microsoft.com/en-us/download/details.aspx?id=19957),是英文的,包括API设计的指导和具体使用async和await的场景,需要的话支持一下,我给大家翻译过来分享一下,谢谢。
Old |
New |
Description |
task.Wait | await task | 等待任务完成 |
task.Result | await task | 获取完成任务的结果 |
Task.WaitAny | await Task.WhenAny | 等待任务集合中的任何一个完成 |
Task.WaitAll | await Task.WhenAll | 等待所有任务都完成 |
Thread.Sleep | await Task.Delay | 等待一段时间 |
Task constructor | Task.Run or TaskFactory.StartNew | 创建一个基于代码的任务 |
Next Steps 下一步
我这里还有一篇文章异步编程最佳实践,进一步解释了“避免async void”,总是”async”和”配置上下文”的指南。
微软的官文也相当不错official MSDN documentation。异步团队也发布了很多关于异步/等待的常见问题,这是继续学习异步的绝好去处。
文章翻译来源:http://blog.stephencleary.com/2012/02/async-and-await.html
Async and Await 异步和等待的更多相关文章
- Async和Await异步编程的原理
1. 简介 从4.0版本开始.NET引入并行编程库,用户能够通过这个库快捷的开发并行计算和并行任务处理的程序.在4.5版本中.NET又引入了Async和Await两个新的关键字,在语言层面对并行编程给 ...
- async And await异步编程活用基础
原文:async And await异步编程活用基础 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希望大家能有所收获,对async 和 await 的理解有更深一层的理解. async 和 a ...
- [.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库
此为文章备份,原文出处(我的网站) [.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库 http://www.dotblogs.com.tw ...
- async 与 await异步编程活用基础
[本文转自:http://www.cnblogs.com/x-xk/archive/2013/06/05/3118005.html 作者:肅] 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希 ...
- C# 异步编程4 async与await 异步程序开发
随着C#异步程序开发系列的深入,你会发现编写异步程序越发简单.事物的发展就是这样的规律,从简单到复杂再到简单. 在C# 5.0中我们可以通过async与await关键字实现快捷的异步程序开发,如下: ...
- Promise、async、await 异步解决方案
参考: https://www.cnblogs.com/CandyManPing/p/9384104.html 或 https://www.jianshu.com/p/fe0159f8beb4(推 ...
- .net4.5使用async和await异步编程实例
关于异步编程的简单理解: 在.NET4.5中新增了异步编程的新特性async和await,使得异步编程更为简单.通过特性可以将这项复杂的工作交给编译器来完成了.之前传统的方式来实现异步编程较为复杂,这 ...
- .NET4.5 异步编程 async和await
msdn介绍:https://msdn.microsoft.com/zh-cn/library/hh191443.aspx 其实很简单,标记了async的方法为异步方法,从方法的左大括号开始同步执行, ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015)
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
随机推荐
- Asp.Net MVC4入门指南(9):查询详细信息和删除记录
在本教程中,您将查看自动生成的Details和Delete方法. 查询详细信息和删除记录 打开Movie控制器并查看Details方法. public ActionResult Details(int ...
- 移动APP开发使用什么样的原型设计工具比较合适?
原型设计工具有Axure,Balsamiq Mockups,JustinMind,iClap原型工具,等其他原型工具.其中JustinMind比较适合APP开发使用. JustinMind可以输出Ht ...
- javascript学习面向对象(二)
主要内容: prototype扩展应用示例: 对比如下: 数组中forEach用法示例: 从上面示例可以看出,forEach只适合遍历一维数组: 应用prototype扩展实现全部元素遍历如下: 简单 ...
- Unity加载模块深度解析(纹理篇)
在游戏和VR项目的研发过程中,加载模块所带来的效率开销和内存占用(即“加载效率”.“场景切换速度”等)经常是开发团队非常头疼的问题,它不仅包括资源的加载耗时,同时也包含场景物件的实例化和资源卸载等.在 ...
- 《uml大战需求分析》阅读笔记05
<uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...
- css怎样让HTML中超出的内容显示为省略号
文字超出了需要隐藏并显示省略号这个在工作中很多时候都要用到,我想很多人都碰到过吧,这个有两种解决方法第一种.用程序开截取字符长度,这个其实也是可以的.第二种就是接下来分享的内容,用css样式来做,话也 ...
- PHP对图片按照一定比例缩放并生成图片文件
list($width, $height)=getimagesize($filename);//缩放比例$per=round(400/$width,3); $n_w=$width*$per;$n_h= ...
- 【洛谷P3076】Taxi
这道题值得好好想一会 我们通过对一些小数据的手算,以及对于每段路程的拆分,可以发现: 1.每个st对应的ed这段路程无论如何都要算上 2.额外还要计算的一段路程,就是"切换"费用 ...
- 清除路由器NAT地址转换
首先当你的NAT网络地址转换成功搭建起来,并且测试过网络通信时,此时NAT地址转换表上面是存在转换信息的,你可以通过在特权模式下输入命令"show ip nat translation&qu ...
- fcc
function spinalCase(str) { if(str.split(/\W|_/).length==1){ for(var i=0;i<str.length;i++){ if(/[A ...