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

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(100); // (Probably should be longer...) // Return a type of "int", not "Task<int>"
return 42;
}

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[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = 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):

Old New Description
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.

My Concurrency Cookbook covers a wide range of use cases for async and await, and also covers situations when you should use the Task Parallel Library, Rx, or TPL Dataflow instead.

(转载:http://blog.stephencleary.com/2012/02/async-and-await.html)

await and async的更多相关文章

  1. 小心C# 5.0 中的await and async模式造成的死锁

    平时在使用C# 5.0中的await and async关键字的时候总是没注意,直到今天在调试一个ASP.NET项目时,发现在调用一个声明为async的方法后,程序老是莫名其妙的被卡住,就算声明为as ...

  2. await和async关键字来写异步程序

    await和async关键字出现于.Net5.0,方便写异步程序. 例子: public class MyClass { public MyClass() { DisplayValue(); //这里 ...

  3. C#同步,异步的理解,包括5.0中await和async(学习笔记)

    之前在工作中一直用的是同步线程,就是先进入画面的load事件,然后在里面进行数据库调用的处理.后面又遇到了公司软件中一些比较古老的代码,一开始在那块古老代码中增加机能的时候,我想用到数据库的数据给画面 ...

  4. 【笔记】记一次.net语法await和async的异步编程实验与笔记。

    1.实践代码全记录: using System; using System.Collections.Generic; using System.Diagnostics; using System.Li ...

  5. await和async更多的理解

    最近有不少网友提起await和async,呵呵,C# 5引进的语法糖. 这个语法糖还真不好吃,能绕倒一堆初学的朋友,在网上也有很多网友关于这块知识点的争论,有对有错,今天在这里把这个误区好好讲讲. 在 ...

  6. C#语法——await与async的正确打开方式

    C#5.0推出了新语法,await与async,但相信大家还是很少使用它们.关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者 ...

  7. C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别

    C#语法——泛型的多种应用   本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...

  8. await和async在一般处理程序中的使用

    写在前面 有个小项目,前端使用的html页面,那服务端的业务处理就采用最简单的一般处理程序了,之前一直在用,觉得一直用一种方式,确实挺蛋疼的,之前也有了解过async和await的内容.就想着自己折腾 ...

  9. C#异步编程のawait和async关键字来写异步程序

    一.await和async关键字 .Net平台不断推出了新的异步编程模型,在.net4.5中加入了关键字await和async,顾名思义,await是指方法执行可等待,即可挂起直到有结果(不是必须立即 ...

  10. .Net 多线程 异步编程 Await、Async和Task

    await和async简介   await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持.主要是解决性能瓶颈,并且增强系统的响应能力. msdn关于 ...

随机推荐

  1. python核心编程第二版练习题答案

    2-5 #写一个while循环,输出整型为0~10 a=0while a<11: print a a+=1 #写一个for循环重复以上操作 for i in range(11): print i ...

  2. 重撸js_2_基础dom操作

    1.node 方法 返回 含义 nodeName String 获取节点名称 nodeType Number 获取节点类型 nodeValue String 节点的值(注意:文本也是节点) 2.inn ...

  3. angular2系列教程(六)两种pipe:函数式编程与面向对象编程

    今天,我们要讲的是angualr2的pipe这个知识点. 例子

  4. 算法与数据结构(十五) 归并排序(Swift 3.0版)

    上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容.归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其拆分的足够小.当拆分的数组中只有一个元素 ...

  5. Mac上MySQL忘记root密码且没有权限的处理办法&workbench的一些tips (转)

    忘记Root密码肿么办 Mac上安装MySQL就不多说了,去mysql的官网上下载最新的mysql包以及workbench,先安装哪个影响都不大.如果你是第一次安装,在mysql安装完成之后,会弹出来 ...

  6. 玩转spring boot——结合AngularJs和JDBC

    参考官方例子:http://spring.io/guides/gs/relational-data-access/ 一.项目准备 在建立mysql数据库后新建表“t_order” ; -- ----- ...

  7. 以项目谈WebGIS中Web制图的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景介绍 一般WebGIS项目中,前端展示数据的流程基本是先做数据入 ...

  8. Asp.Net WebApi核心对象解析(上篇)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  9. 基于window7+caffe实现图像艺术风格转换style-transfer

    这个是在去年微博里面非常流行的,在git_hub上的代码是https://github.com/fzliu/style-transfer 比如这是梵高的画 这是你自己的照片 然后你想生成这样 怎么实现 ...

  10. C# BackgroundWorker 详解

    在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题.解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完 ...