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 ...
随机推荐
- AngularJs自定义指令详解(4) - transclude
transclude默认值为false,如果设置 transclude为true,那么相应地,必须在模板代码中加入ng-transclude指令. 先看个例子: <!DOCTYPE html&g ...
- MVC的自定义动作过滤器(一)
感谢好朋友wolfy在园子里的很多有价值的文章,方便了很多朋友,向榜样学习,开始自己的总结之旅:) 遇到问题: 1.http://q.cnblogs.com/q/67382/#a_150210 //添 ...
- Linux下高cpu占有率的调试方案
1.用top命令查看哪个进程占用CPU高 gateway网关进程14094占用CPU高达891%,这个数值是进程内各个线程占用CPU的累加值. 2.用top -H -p pid命令查看进程内各个线 ...
- 下拉框数据的动态选择,类似级联ajax刷新数据
简单的两个下拉列表,第二个中的数据与第一个下拉框相关: --------------------var selected = $(this).children('option:selected').v ...
- VMware 12 的vmware tools安装和如何使用(系统是CENTOS6.5)
1.用了一下虚拟机vmware12,但是总是没法使用它的vmware Tool ,网上一直说在哪个哪个文件夹,其实并没有 2.于是我用命令行找到了在系统中的VMware Tools 3.首先,保证li ...
- C# 部分语法总结(入门经典)
class Program { static void Main(string[] args) { init(); System.Console.ReadKey(); } #region 接口 /// ...
- Android广播大全
1.String ADD_SHORTCUT_ACTION 动作:在系统中添加一个快捷方式. 2.String ALL_APPS_ACTION 动作:列举所有可用的应用.输入:无. 3.String A ...
- 数据库软件dbForge Studio for MySQL更新至v.6.1
本文转自:慧都控件网 说到MariaDB,这个数据库算是MySQL的一个分支.现在非常的流行,很多地方都能看到它的身影.MariaDB作为一种新的数据库管理系统,在短时间内获得如此高的关注度.这也是D ...
- linux系统安装软件方法大全
1.源代码包的安装gzip -d apache_1.3.20.tar.gz (解压)tar xvf apache_1.3.20.tar (解包)cd apache_1.3.20 ./configure ...
- 更改Xampp-sql的默认密码-配置appche运行环境
用php编写的web应用程序,需运行在php的web容器中,其中apache server是一个针对php web容器,它是apache下的开源项目.通常要运行一个web程序,我们还需要安装数据库软件 ...