https://msdn.microsoft.com/zh-cn/library/mt674882.aspx

侵删

更新于:2015年6月20日

欲获得最新的Visual Studio 2017 RC文档,参考Visual Studio 2017 RC Documentation

使用异步编程,你可以避免性能瓶颈和提升总体相应效率。然而,传统的异步方法代码的编写方式比较复杂,导致它很难编写,调试和维护。

Visual Studio 2012引入了一个简单的异步编程的方法,依赖.NET Framework 4.5和对应的运行时版本的支持。这样,开发者的一些复杂的工作就交给了编译器去完成,并且使你的代码保证了同步编程代码一样的逻辑结构。这样你就可以省了很多编写异步代码的时间。

本节主要介绍了何时和如何使用异步编程,包含了一些链接来展示详情和例子。

使用异步来提升相应性

异步编程对于一些存在潜在的阻塞可能的行为很重要,例如当你的应用程序需要访问网络资源的时候。连接到网络资源有时候很慢或者有延迟,这时如果这个访问时同步的,整个应用程序就要等待这个行为的结束。如果网络访问行为是异步的,这个应用程序就可以同时执行其他不依赖网络资源的工作直到这个潜在的阻塞行为结束。

下面的表格展示了一些异步编程提升响应性的场景。其中列出的.NET Framework 4.5和运行时的API包含的方法支持异步编程。

应用程序功能

支持的包含异步方法的API

网络连接

HttpClient, SyndicationClient

文件资源

StorageFile, StreamWriter, StreamReader, XmlReader

图片

MediaCapture, BitmapEncoder, BitmapDecoder

WCF

Synchronous and Asynchronous Operations

特别是在能够访问UI线程的应用程序中,异步编程非常有价值。因为所有与UI有关的行为都共享一个线程,如果有任何一个线程在同步进行的时候被阻塞,你的应用程序就会停止响应,尽管应用程序只是在等待,你却仍然以为它已经挂了。

当你使用异步方法, 应用程序会继续相应UI。你可以改变窗口大小或者最小化它,例如你可以关闭应用程序,如果你不想等它结束的话。

这种基于异步的编程方法让你在编写异步的时候多了一个选择——它相当于提供了一个自动转化机制。也就是说,你可以花更少的精力来完成传统异步编程的效果。

异步的方法更加容易编写

C#中的asyncawait关键字是异步编程的核心。通过使用这两个关键字,你可以使用 .NET Framework 或者运行时的资源来创造异步方法——几乎和你写同步方法一样。你通过使用async和await定义的方法就相当于异步方法。

下面的例子展示了一个异步方法。几乎所有的代码都看上去那么熟悉。注释段解释了一些你来用完成整个异步编程添加的特性。

在这个主题的结尾,你可以找到一个完整的WPF例子代码,你可以从Async Sample: Example from "Asynchronous Programming with Async and Await"下载。

// 在方法签名中需要注意的三个地方:
// - 方法必须有async修饰.
// - 返回类型必须是Task或者Task<T>(看 "Return Types" 小节.)
// 这里是Task<int>因为返回声明返回了一个整型.
// - 方法名以async结尾
async Task<int> AccessTheWebAsync()
{
// 你需添加一个对System.Net.Http的引用来声明client
    HttpClient client = new HttpClient();  

    // GetStringAsync方法返回一个Task<string>.这个意味着当你await这个
// 任务的时候,你会得到一个string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // 你可以完成一些不依赖GetStringAsync方法返回的字符串的任务
DoIndependentWork(); // await操作符中止了AccessTheWebAsync方法.
// - AccessTheWebAsync 无法继续知道 getStringTask 完成.
// - 与此同时,控制器返回AccessTheWebAsync的调用.
// - 当 getStringTask 完成的时候,控制器继续进行任务.
// - await操作符获得getStringTask返回的字符串.
string urlContents = await getStringTask; // 返回声明指定了一个整型结果
// 任何awaiting AccessTheWebAsync的方法都会得到这个长度值
return urlContents.Length;
}

在开始调用GetStringAsync 和await这个方法完成之间,如果AccessTheWebAsync方法不包含任何其他的工作的话,你可以通过调用和await下面的声明来简化你的代码。

string urlContents = await client.GetStringAsync();  

下面总结了之前例子中异步方法的特性:

  • 方法签名包含async 修饰符
  • 异步方法名称按照惯例以Async结尾
  • 返回值是以下几种类型之一:
  • Task<TResult>:如果你的方法包含一个操作符是TResult的返回声明
  • Task :如果你的方法没有任何返回值或者返回声明里面没有操作符
  • Void:如果你在写一个异步事件句柄

更多的信息可以在下面的“返回值类型和参数”获得。

  • 方法通常包含至少一个await表达式,这个await在方法标记了一个方法无法继续执行,直到需要等待的方法完成的点。在这个等待期间,方法是被暂停着的,控制器返回到方法的调用者。下一个小节会介绍这个暂停点都发生了什么。

在异步方法中,你使用这些关键字和类型去指定要做什么,编译器就会完成剩下的工作,包括继续追踪控制器在暂停的方法中返回之后要发生什么。一些例程,例如循环和异常处理,在传统的异步编程代码中很难被处理。在一个async方法中,你就像写同步代码一样地去编程,就能解决问题了。

更多的关于之前.NET Framework版本中的异步信息,参考TPL and Traditional .NET Framework Asynchronous Programming

在一个异步方法中发生了什么

在异步编程中最重要的事情就是了解控制流如何在方法与方法之间移动,下图展示了这个过程:

图中的数字对应以下的步骤:

  • 一个事件句柄被调用然后等待AccessTheWebAsync 这个异步方法。
  • AccessTheWebAsync 创建了一个HttpClient实例,然后调用GetStringAsync 异步方法去把一个网页以字符串形式下载。
  • GetStringAsync 方法中发生了一些事情使这个方法停止了,可能它需要等待网站响应或者其他的一些阻塞动作。为了防止阻塞资源,GetStringAsync 方法把控制还给了它的调用者AccessTheWebAsync。GetStringAsync 返回了一个Task<TResult> ,TResult是一个string类型,AccessTheWebAsync 方法把任务交给了getStringTask 变量。这个task表示正在进行的GetStringAsync的调用,当这个调用完成之后会返回一个string的值。
  • 因为getStringTask 还在await中,所以AccessTheWebAsync 可以继续做其他不依赖GetStringAsync返回值的工作。这个工作可以用DoIndependentWork的调用来表示。
  • DoIndependentWork 是一个完成其他的工作然后返回到它的调用者的同步方法。
  • AccessTheWebAsync 已经完成了不需要getStringTask返回值的工作。AccessTheWebAsync 接下来想要计算并且返回已经下载的string长度,但是这个方法无法计算,直到getStringTask返回一个字符串。因此,AccessTheWebAsync 使用了一个await操作符来停止整个AccessTheWebAsync方法的调用。AccessTheWebAsync返回一个Task<int>给调用者,task意味着一个产生字符串长度的承诺。注意,如果GetStringAsync在AccessTheWebAsync等待它之前结束,控制器仍然在AccessTheWebAsync方法之中。如果getStringTask已经完成并且AccessTheWebSync不需要等待它的结果的话,暂停然后AccessTheWebAsync方法返回的异步开销就会被浪费。 在调用者内部(这个例子中的事件句柄),方法继续运行。在方法await之前,调用者可能会进行其他不需要依赖AccessTheWebAsync返回的工作,或者如果没有其他工作的话调用者可能立即await。这个事件句柄等待AccessTheWebAsync方法,AccessTheWebAsync方法等待GetStringAsync方法。
  • GetStringAsync 完成并且产生了一个string返回值。这个string返回值 并不是你想的那种返回给GetStringAsync调用者的值(要记住这个方法已经在步骤3的时候返回了一个task)。不同的是,这个string返回值被保存在task中,这个task表示getStringTask方法的结束。await操作符获得getStringTask的返回结果。赋值声明把这个结果赋值给urlContents变量。
  • 当AccessTheWebAsync得到了string结果之后,它就可以计算string长度。 AccessTheWebAsync的工作也完成了,这个等待着的句柄就可以继续。在这个专题结尾的例子中,你就可以确定这个事件句柄获得并且打印出了string返回值的长度。

如果你是异步编程的初学者,那就花一点事件考虑同步和异步编程的不同之处。一个同步方法只有等到它的工作都完成了(步骤5)才会返回,而一个异步方法在等待的时候(步骤3和6)会返回一个task。当异步方法最终完成他的工作的时候,task会被标记成完成,并且它的结果,如果有的话,会被保存在task中。

想获得更多的信息,请参阅Control Flow in Async Programs (C#)

异步方法的API

你可能会想知道去哪里找到类似于GetStringAsync 的异步方法。.NET Framework 4.5或者更高的版本都支持async和await。你可以通过观察方法名后面是否存在“Async”后缀,返回类型是Task 还是Task<TResult>来判断他们是不是异步的。例如,System.IO.Stream 类就包含类似于CopyToAsync, ReadAsyncWriteAsync 的方法,作为CopyTo, ReadWrite的补充。

运行时也包含很多你可以在窗口应用程序中使用async和await的方法。更多信息和例子,参阅Quickstart: using the await operator for asynchronous programming, Asynchronous programming (Windows Store apps), and WhenAny: Bridging between the .NET Framework and the Windows Runtime (C#).

线程

异步方法是一个非阻塞的操作。当await的任务运行的时候,异步方法中的await表达式不会阻塞当前线程。并且这个表达式会继续执行方法中剩下的代码然后把控制还给异步方法的调用者。

async和await关键字不会生成额外的线程。异步方法不需要多线程,因为一个异步方法并不是在它自己的线程中运行的。这个方法在当前的同步上下文中运行,并且仅仅在这个方法被激活的时候才耗费时间。你可以使用Task.Run 来把对CPU性能敏感的任务设置成后台线程,但是后台线程不能等待其他任务完成再继续执行。

几乎所有情况下都建议使用基于async的异步编程方法。而且这种方式比对IO性能敏感的BackgroundWorker更好,因为代码更加简单并且你不需要去关心资源争夺的情况。和 Task.Run结合使用的话,异步编程比对CPU性能敏感的BackgroundWorker更好,因为异步编程将Task.Run分配到线程池的工作和你代码中的线程协调细节分开。

async和await

如果你使用async修饰符来定义一个方法的话,方法就具有以下两个功能:

  • 异步方法可以使用await来指定一个等待点。await操作符告诉编译器这个异步方法不能通过,直到需要等待的方法执行完毕。与此同时,控制器返回到该方法调用者。

async方法中的await造成的停止,不会导致方法的推出,并且finally语句块不会执行。

  • 异步方法可以被调用它的方法await

一个异步方法通常包含一个或者多个await操作符,但是await操作符就算不存在也不会造成编译错误。如果一个异步方法没有使用await操作符的话,整个方法就会和同步方法一样,除了有一个async操作符。编译器会对这个方法发出一个警告。

async和await是上下文关键字,获取更多相关的信息和例子,可以参考:

返回值类型和参数

在.NET Framework中一个异步方法一般会返回一个Task 或者一个 Task<TResult>类型。在异步方法中,一个await操作符应用在一个从其他异步方法返回的任务中。

如果方法包含指定了TResult类型的 return 声明,你可以定义一个Task<TResult>作为返回值。

如果你的方法没有返回声明或者声明中不返回操作符,你可以使用Task作为返回值。

下面的例子展示如何声明和调用返回一个Task 或者一个 Task<TResult>的例子:

// 签名中制定一个 Task<TResult>  类型
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// 返回声明中制定一个返回类型
return hours;
} // 调用TResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// 或者使用一个单行的声明
int intResult = await TaskOfTResult_MethodAsync(); // 签名指定一个 Task
async Task Task_MethodAsync()
{
// . . .
// 这个方法没有返回声明
} // 调用ethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
//或者使用一个单行声明
 await Task_MethodAsync(); 

每一个返回的task都代表一个正在进行的工作。一个task包括了这个异步方法的运行状态和最终的结果或者抛出的异常。

一个异步方法也可以有一个void的返回类型。这个返回类型主要在定义事件句柄的时候用到,对于定义事件句柄,void的返回类型是必须的。异步事件句柄经常作为异步程序的起点。

一个void返回类型的异步方法不能被await并且这个方法的调用者不能捕获这个方法抛出的异常。

一个异步方法不能声明 ref 或者out 参数,但是这个方法可以调用包含这种类型参数的方法。

参考Async Return Types (C#)来获取更多的相关信息和例子。更多的关于如何铺货异步方法中的异常信息,参考try-catch

运行时中的异步API包含了以下其中一种的返回类型,他们都类似于task:

想获得更多信息,参见 Quickstart: using the await operator for asynchronous programming.

命名惯例

按照惯例,你要在异步方法中添加async 修饰符。

当一个事件,基类或者接口暗示了其他的名字,例如你不可以重命名一个事件句柄,例如Button1_Click,你可以忽视这个惯例。(蛤??)

相关的主题和例子(Visual Studio

标题 介绍 例子
Walkthrough: Accessing the Web by Using async and await (C#) 展示了如何把一个同步的WPF解决方案改成一个异步WPF解决方案。这个应用程序下载了几个页面。 Async Sample: Accessing the Web Walkthrough
How to: Extend the async Walkthrough by Using Task.WhenAll (C#) 添加了Task.WhenAll到之前的修改中。WhenAll让所有的下载同时开始。  
How to: Make Multiple Web Requests in Parallel by Using async and await (C#) 介绍了如何同时开启多个任务。

Async Sample: Make Multiple Web Requests in Parallel

Async Return Types (C#) 介绍了async方法可以返回的类型并且介绍了何时应该返回何种类型比较合适  
Control Flow in Async Programs (C#) 在一个异步程序中追溯一系列await表达式中的控制流 Async Sample: Control Flow in Async Programs
Fine-Tuning Your Async Application (C#) 展示如何在你的异步解决方案中添加以下的功能
- Cancel an Async Task or a List of Tasks (C#)
- Cancel Async Tasks after a Period of Time (C#)
- Cancel Remaining Async Tasks after One Is Complete (C#)
- Start Multiple Async Tasks and Process Them As They Complete (C#)
Async Sample: Fine Tuning Your Application
Handling Reentrancy in Async Apps (C#) 展示了如何当异步操作在执行的时候重新启动如何处理  
WhenAny: Bridging between the .NET Framework and the Windows Runtime (C#) 展示了如何在.NET Framework和运行时的IAsyncOperations中关联两个task类型,因此你可以和运行时方法搭配使用WhenAny<TResult> Async Sample: Bridging between .NET and Windows Runtime (AsTask and WhenAny)
Async Cancellation: Bridging between the .NET Framework and the Windows Runtime (C#) 展示了如何在.NET Framework和运行时的IAsyncOperations中关联两个task类型,因此你可以和运行时方法搭配使用CancellationTokenSource Async Sample: Bridging between .NET and Windows Runtime (AsTask & Cancellation)
Using Async for File Access (C#) 列举展示了使用async和await来获取文件的好处。  
Task-based Asynchronous Pattern (TAP) 介绍了.NET Framework中的一个新的异步模式。
这种模式基于TaskTask<TResult> 类型
 
Async Videos on Channel 9 提供了一些关于异步编程的视频  

一个例子

下面是之前提到的WPF的项目中MainWindow.xaml.cs中的一段代码。你可以从Async Sample: Example from "Asynchronous Programming with Async and Await"下载这个例子。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; // Add a using directive and a reference for System.Net.Http;
using System.Net.Http; namespace AsyncFirstExample
{
public partial class MainWindow : Window
{
// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// Call and await separately.
//Task<int> getLengthTask = AccessTheWebAsync();
//// You can do independent work here.
//int contentLength = await getLengthTask; int contentLength = await AccessTheWebAsync(); resultsTextBox.Text +=
String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
} // Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient(); // GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork(); // The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask; // The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
} void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
}
} // Sample Output: // Working . . . . . . . // Length of the downloaded string: 41564.

C#的多线程——使用async和await来完成异步编程(Asynchronous Programming with async and await)的更多相关文章

  1. async和await关键字实现异步编程

    async和await关键字实现异步编程 异步编程   概念 异步编程核心为异步操作,该操作一旦启动将在一段时间内完成.所谓异步,关键是实现了两点:(1)正在执行的此操作,不会阻塞原来的线程(2)一旦 ...

  2. async/await actor promise 异步编程

    Python协程:从yield/send到async/await http://blog.guoyb.com/2016/07/03/python-coroutine/ Async/Await替代Pro ...

  3. 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法

    什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...

  4. Asynchronous programming with async and await (C#)

    Asynchronous Programming with async and await (C#) | Microsoft Docs https://docs.microsoft.com/en-us ...

  5. 【C# TAP 异步编程】三、async\await的运作机理详解

    [原创] 本文只是个人笔记,很多错误,欢迎指出. 环境:vs2022  .net6.0 C#10 参考:https://blog.csdn.net/brook_shi/article/details/ ...

  6. js异步编程终级解决方案 async/await

      在最新的ES7(ES2017)中提出的前端异步特性:async.await. async.await是什么 async顾名思义是“异步”的意思,async用于声明一个函数是异步的.而await从字 ...

  7. C#异步编程学习笔记之-async和await

    一.异步方法介绍(async和await):如果使用async修饰符将某种方法指定为异步方法,即启用以下两种功能.1.标记的异步方法可以使用await来指定暂停点.await运算符通知编译器异步方法: ...

  8. C#进阶——从应用上理解异步编程的作用(async / await)

    欢迎来到学习摆脱又加深内卷篇 下面是学习异步编程的应用 1.首先,我们建一个winfrom的项目,界面如下: 2.然后先写一个耗时函数: /// <summary> /// 耗时工作 // ...

  9. 谈谈C#多线程开发:并行、并发与异步编程

    阅读导航 一.使用Task 二.并行编程 三.线程同步 四.异步编程模型 五.多线程数据安全 六.异常处理 概述 现代程序开发过程中不可避免会使用到多线程相关的技术,之所以要使用多线程,主要原因或目的 ...

随机推荐

  1. 上手Caffe(二)

    @author:oneBite本文简述如何在windows环境下,运行caffe的“hello world”例程体会适用caffe的流程:转换输入数据格式>在solver.prototxt中配置 ...

  2. Android程序猿必须警示的13个坑

        Android开发中,犯错是难免的,不犯错是不正常的,但是犯了错以后,我们必须时刻谨记这些坑,避免再次被坑,下面小编整理了13个,日常工作中,比较常见且易犯的错误,分享给大家.    1.类的 ...

  3. java中unmodifiableList方法的应用场景

    java对象中primitive类型变量可以通过不提供set方法保证不被修改,但对象的List成员在提供get方法后,就可以随意add.remove改变其结构,这不是希望的结果.网上看了下,发现Col ...

  4. VS2013下配置OpenCV 3.0.0 &&& VS2013下配置Opencv2.4.9

    最近做图像需要用到Matlab和OpenCV,一些东西真的是要深入的研究进去才会有所发现,但Matlab和C++都不是我擅长的语言,所以要很加油很加油才行啊!! 步入正题. 1.  环境:Win7 6 ...

  5. 设置RobotFramework的ftplibrary中,将Upload_file操作的异常改为回显错误信息。

    测试中需要通过FTP通道,将数据发送给服务器,而这个上传的数据要被阻断.在结合RobotFramework测试中,安装的ftplibrary,使用upload_file操作,如果上传动作失败,会抛出异 ...

  6. CSU 2031

    2031: Barareh on Fire Submit Page   Summary   Time Limit: 3 Sec     Memory Limit: 512 Mb     Submitt ...

  7. MAC中如何配置两套android-sdk环境

    1 背景介绍 随着android studio的完善和普及,越来越多Android app开发者从Eclipse+ADT环境转到Android Studio(后文统一用AS表示).但是,AS往往需要较 ...

  8. WinDirStat is a disk usage statistics viewer

    WinDirStat is a disk usage statistics viewer and cleanup tool for various versions of Microsoft Wind ...

  9. Debian中文字体安装

    默认装的英文办的debian7,看中国字不太美,这好办照着做吧 1. Setup locales #dpkg-reconfigure locales 选择 zh_CN GB2312 zh_CN.GBK ...

  10. Linux下的ACL

    ACL理论概述 9位的属主/属组/其他人访问控制系统已经得到证明是强大的,足以满足大多数管理方面的需求. 事实上,在所有非UNIX操作系统上都采用了一种实质上更为复杂的方式来管理对于文件的访问:访问控 ...