https://blog.stephencleary.com/2012/02/async-and-await.html

Most people have already heard about the new “async” and “await” functionality coming in Visual Studio 11. This is Yet Another Introductory Post.

First, the punchline: Async will fundamentally change the way most code is written.

Yup, I believe async/await will have a bigger impact than LINQ. Understanding async will be a basic necessity just a few short years from now.

Introducing the Keywords

Let’s dive right in. I’ll use some concepts that I’ll expound on later on - just hold on for this first part.

Asynchronous methods look something like this:

public async Task DoSomethingAsync()
{
// In the Real World, we would actually do something...
// For this example, we're just going to (asynchronously) wait 100ms.
await Task.Delay();
}

The “async” keyword enables the “await” keyword in that method and changes how method results are handled. That’s all the async keyword does! It does not run this method on a thread pool thread, or do any other kind of magic. The async keyword only enables the await keyword (and manages the method results).

The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an “await” (or throws an exception).

The “await” keyword is where things can get asynchronous. Await is like a unary一元的 operator: it takes a single argument, an awaitable (an “awaitable” is an asynchronous operation).

Await examines that awaitable to see if it has already completed;

if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).

If “await” sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method.

Later on, when the awaitable completes, it will execute the remainder of the async method. If you’re awaiting a built-in awaitable (such as a task), then the remainder of the async method will execute on a “context” that was captured before the “await” returned.

I like to think of “await” as an “asynchronous wait”. That is to say, the async method pauses until the awaitable is complete (so it waits), but the actual thread is not blocked (so it’s asynchronous).

Awaitables

As I mentioned, “await” takes a single argument - an “awaitable” - which is an asynchronous operation. There are two awaitable types already common in the .NET framework: Task<T> and Task.

There are also other awaitable types: special methods such as “Task.Yield” return awaitables that are not Tasks, and the WinRT runtime (coming in Windows 8) has an unmanaged awaitable type. You can also create your own awaitable (usually for performance reasons), or use extension methods to make a non-awaitable type awaitable.

That’s all I’m going to say about making your own awaitables. I’ve only had to write a couple of awaitables in the entire time I’ve used async/await. If you want to know more about writing your own awaitables, see the Parallel Team Blog or Jon Skeet’s Blog.

One important point about awaitables is this: it is the type that is awaitable, not the method returning the type. In other words, you can await the result of an async method that returns Task … because the method returns Task, not because it’s async. So you can also await the result of a non-async method that returns Task:

public async Task NewStuffAsync()
{
// Use await and have fun with the new stuff.
await ...
} public Task MyOldTaskParallelLibraryCode()
{
// Note that this is not an async method, so we can't use await in here.
...
} public async Task ComposeAsync()
{
// We can await Tasks, regardless of where they come from.
await NewStuffAsync();
await MyOldTaskParallelLibraryCode();
}

Tip: If you have a very simple asynchronous method, you may be able to write it without using the await keyword (e.g., using Task.FromResult).

If you can write it without await, then you should write it without await, and remove the async keyword from the method.

A non-async method returning Task.FromResult is more efficient than an async method returning a value.

Return Types

Async methods can return Task<T>, Task, or void. In almost all cases, you want to return Task<T> or Task, and return void only when you have to.

Why return Task<T> or Task? Because they’re awaitable, and void is not. So if you have an async method returning Task<T> or Task, then you can pass the result to await. With a void method, you don’t have anything to pass to await.

You have to return void when you have async event handlers.

You can also use async void for other “top-level” kinds of actions - e.g., a single “static async void MainAsync()” for Console programs.

However, this use of async void has its own problem; see Async Console Programs. The primary use case for async void methods is event handlers.

Returning Values

Async methods returning Task or void do not have a return value. Async methods returning Task<T> must return a value of type T:

public async Task<int> CalculateAnswer()
{
await Task.Delay(); // (Probably should be longer...) // Return a type of "int", not "Task<int>"
return ;
}

This is a bit odd to get used to, but there are good reasons behind this design.

Context

In the overview, I mentioned that when you await a built-in awaitable, then the awaitable will capture the current “context” and later apply it to the remainder of the async method. What exactly is that “context”?

Simple answer:

  1. If you’re on a UI thread, then it’s a UI context.
  2. If you’re responding to an ASP.NET request, then it’s an ASP.NET request context.
  3. Otherwise, it’s usually a thread pool context.

Complex answer:

  1. If SynchronizationContext.Current is not null, then it’s the current SynchronizationContext. (UI and ASP.NET request contexts are SynchronizationContext contexts).
  2. Otherwise, it’s the current TaskScheduler (TaskScheduler.Default is the thread pool context).

What does this mean in the real world? For one thing, capturing (and restoring) the UI/ASP.NET context is done transparently:

// WinForms example (it works exactly the same for WPF).
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by the file download.
await DownloadFileAsync(fileNameTextBox.Text); // Since we resume on the UI context, we can directly access UI elements.
resultTextBox.Text = "File downloaded!";
} // ASP.NET example
protected async void MyButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the ASP.NET thread is not blocked by the file download.
// This allows the thread to handle other requests while we're waiting.
await DownloadFileAsync(...); // Since we resume on the ASP.NET context, we can access the current request.
// We may actually be on another *thread*, but we have the same ASP.NET request context.
Response.Write("File downloaded!");
}

This is great for event handlers, but it turns out to not be what you want for most other code (which is, really, most of the async code you’ll be writing).

Avoiding Context

Most of the time, you don’t need to sync back to the “main” context.

Most async methods will be designed with composition in mind: they await other operations, and each one represents an asynchronous operation itself (which can be composed by others).

In this case, you want to tell the awaiter to not capture the current context by calling ConfigureAwait and passing false, e.g.:

private async Task DownloadFileAsync(string fileName)
{
// Use HttpClient or whatever to download the file contents.
var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false); // Note that because of the ConfigureAwait(false), we are not on the original context here.
// Instead, we're running on the thread pool. // Write the file contents out to a disk file.
await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false); // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice.
} // WinForms example (it works exactly the same for WPF).
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by the file download.
await DownloadFileAsync(fileNameTextBox.Text); // Since we resume on the UI context, we can directly access UI elements.
resultTextBox.Text = "File downloaded!";
}

The important thing to note with this example is that each “level” of async method calls has its own context.

DownloadFileButton_Click started in the UI context, and called DownloadFileAsync. DownloadFileAsync also started in the UI context, but then stepped out of its context by calling ConfigureAwait(false).

The rest of DownloadFileAsync runs in the thread pool context. However, when DownloadFileAsync completes and DownloadFileButton_Click resumes, it does resume in the UI context.

A good rule of thumb is to use ConfigureAwait(false) unless you know you do need the context.

Async Composition

So far, we’ve only considered serial composition: an async method waits for one operation at a time.

It’s also possible to start several operations and await for one (or all) of them to complete.

You can do this by starting the operations but not awaiting them until later:

public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[];
tasks[] = DoOperation0Async();
tasks[] = DoOperation1Async();
tasks[] = DoOperation2Async(); // At this point, all three tasks are running at the same time. // Now, we await them all.
await Task.WhenAll(tasks);
} public async Task<int> GetFirstToRespondAsync()
{
// Call two web services; take the first response.
Task<int>[] tasks = new[] { WebService1Async(), WebService2Async() }; // Await for the first one to respond.
Task<int> firstTask = await Task.WhenAny(tasks); // Return the result.
return await firstTask;
}

By using concurrent composition作曲(协调) (Task.WhenAll or Task.WhenAny), you can perform simple concurrent operations.

You can also use these methods along with Task.Run to do simple parallel computation.

However, this is not a substitute for the Task Parallel Library - any advanced CPU-intensive parallel operations should be done with the TPL.

Guidelines

Read the Task-based Asynchronous Pattern (TAP) document. It is extremely well-written, and includes guidance on API design and the proper use of async/await (including cancellation and progress reporting).

There are many new await-friendly techniques that should be used instead of the old blocking techniques. If you have any of these Old examples in your new async code, you’re Doing It Wrong(TM):

task.Wait await task Wait/await for a task to complete
task.Result await task Get the result of a completed task
Task.WaitAny await Task.WhenAny Wait/await for one of a collection of tasks to complete
Task.WaitAll await Task.WhenAll Wait/await for every one of a collection of tasks to complete
Thread.Sleep await Task.Delay Wait/await for a period of time
Task constructor Task.Run or TaskFactory.StartNew Create a code-based task

Next Steps

I have published an MSDN article Best Practices in Asynchronous Programming, which further explains the “avoid async void”, “async all the way” and “configure context” guidelines.

The official MSDN documentation is quite good; they include an online version of the Task-based Asynchronous Pattern document which is excellent, covering the designs of asynchronous methods.

The async team has published an async/await FAQ that is a great place to continue learning about async. They have pointers to the best blog posts and videos on there. Also, pretty much any blog post by Stephen Toub is instructive!

Of course, another resource is my own blog.

Async and Await (Stephen Cleary)的更多相关文章

  1. 为什么我们要使用Async、Await关键字

    前不久,在工作中由于默认(xihuan)使用Async.Await关键字受到了很多质问,所以由此引发这篇博文“为什么我们要用Async/Await关键字”,请听下面分解: Async/Await关键字 ...

  2. (译)关于async与await的FAQ

    传送门:异步编程系列目录…… 环境:VS2012(尽管System.Threading.Tasks在.net4.0就引入,在.net4.5中为其增加了更丰富的API及性能提升,另外关键字”async” ...

  3. Async and Await

    http://blog.stephencleary.com/2012/02/async-and-await.html Most people have already heard about the ...

  4. Async Performance: Understanding the Costs of Async and Await

    Stephen Toub Download the Code Sample Asynchronous programming has long been the realm of only the m ...

  5. [译] C# 5.0 中的 Async 和 Await (整理中...)

    C# 5.0 中的 Async 和 Await [博主]反骨仔 [本文]http://www.cnblogs.com/liqingwen/p/6069062.html 伴随着 .NET 4.5 和 V ...

  6. 探索c#之Async、Await剖析

    阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...

  7. Async和Await异步编程的原理

    1. 简介 从4.0版本开始.NET引入并行编程库,用户能够通过这个库快捷的开发并行计算和并行任务处理的程序.在4.5版本中.NET又引入了Async和Await两个新的关键字,在语言层面对并行编程给 ...

  8. 异步方法的意义何在,Async和await以及Task的爱恨情仇,还有多线程那一家子。

    前两天刚感受了下泛型接口的in和out,昨天就开始感受神奇的异步方法Async/await,当然顺路也看了眼多线程那几个.其实多线程异步相关的类单个用法和理解都不算困难,但是异步方法Async/awa ...

  9. 多线程之异步编程: 经典和最新的异步编程模型,async与await

    经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...

随机推荐

  1. PHP代码多人开发

    我们一定遇到过这样的情况:许多项目是由多个人同时开发的,涉及到不同的开发环境和测试环境我们的许多PHP工程师在windows上开发代码虽然nginx+PHP+MySQL的环境也可以在windows上配 ...

  2. HTML中关于 浮动 的简单说明

    1.首先,标签之所以有存在等级分类,是因为他们处于标准文档流(块级元素,行内元素,行内块元素)当中. 2.如何脱离标准文档流? 浮动 绝对定位 固定定位 这些可以让一个标签脱离标准文档流,而元素一旦脱 ...

  3. 什么?在SAP中国研究院里还需要会PHP开发?

    请原谅Jerry这篇文章的题目有"标题党"的嫌疑. 没错,至少SAP成都研究院的部分团队如此,比如Jerry目前就职的SAP成都研究院数字创新空间. 之前Jerry的文章, 曾经提 ...

  4. Java Socket编程----网络基础

    详见:https://www.cnblogs.com/rocomp/p/4790340.html Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而 ...

  5. 使用Linux的tcpdump命令结合Windows的wireshark抓包和分析

    tcpdump简介 tcpdump是Linux系统下的一款抓包命令集,工作原理是基于网卡抓取流动在网卡上的数据包.在Linux系统中由于tcpdump命令的简单和强大,我们一般直接使用tcpdump命 ...

  6. MySQL授权(用户权限)

    一.mysql查询与权限 (二)授权 用户管理: 设置用户密码 前期准备工作: 停止服务 将配置文件当中的skip-grant-tables删除掉 重启服务: 执行修改命令 查看用户状态(如果数据过多 ...

  7. php curl请求接口并获取数据

    当我们在做php开发的时候,很多时候需要对接口进行测试,或者更方便的调用一些已有模块的接口,取到结果并进行后续操作,我们可以通过curl进行模拟提交post和get请求,来去实现这些功能. 下面是对c ...

  8. 用js刷剑指offer(二进制中一的个数)

    题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 牛客网链接 思路 如果一个整数不为0,那么这个整数至少有一位是1.如果我们把这个整数减1,那么原来处在整数最右边的1就会变为 ...

  9. php截取特定字符前面或后面的内容

    1.php 截取特定字符后面的内容 可以使用函数strripos,获取一个字符串在另一个字符串中第一次出现的位置. $number = '1_0'; $result = substr($number, ...

  10. python+Appium自动化:日志logging模块

    日志级别 debug.info.warn.error.critical五个级别 logging模块构成(四部分) logger(记录器,用于日志采集) Handler(处理器,将日志记录发送到合适的路 ...