基于任务的异步模式 (TAP)Task-based Asynchronous Pattern (TAP)

基于任务的异步模式 (TAP) 是基于 System.Threading.Tasks.Task 命名空间中的 System.Threading.Tasks.Task<TResult>System.Threading.Tasks 类型,这些类型用于表示任意异步操作。The Task-based Asynchronous Pattern (TAP) is based on the System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> types in the System.Threading.Tasks namespace, which are used to represent arbitrary asynchronous operations. TAP 是用于新开发的建议的异步设计模式。TAP is the recommended asynchronous design pattern for new development.

命名、参数和返回类型Naming, Parameters, and Return Types

TAP 使用单个方法表示异步操作的开始和完成。TAP uses a single method to represent the initiation and completion of an asynchronous operation. 这与异步编程模型(APM 或 IAsyncResult)模式相反(该模式要求 BeginEnd 方法),并与基于事件的异步模式 (EAP) 相反(该模式要求具有 Async 后缀的方法,还要求一个或多个事件、事件处理程序委托类型和 EventArg 派生类型)。This is in contrast to the Asynchronous Programming Model (APM or IAsyncResult) pattern, which requires Begin and End methods, and in contrast to the Event-based Asynchronous Pattern (EAP), which requires a method that has the Async suffix and also requires one or more events, event handler delegate types, and EventArg-derived types. TAP 中的异步方法在操作名称后面添加 Async 后缀;例如,Get 操作的 GetAsyncAsynchronous methods in TAP include the Async suffix after the operation name; for example, GetAsync for a Get operation. 如果你正在将 TAP 方法添加到已包含具有 Async 后缀的方法名称的类中,请改用后缀 TaskAsyncIf you're adding a TAP method to a class that already contains that method name with the Async suffix, use the suffix TaskAsync instead. 例如,如果类具有 GetAsync 方法,请使用名称 GetTaskAsyncFor example, if the class already has a GetAsync method, use the name GetTaskAsync.

TAP 方法返回 System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult>,具体取决于相应同步方法返回的是 void 还是类型 TResultA TAP method returns either a System.Threading.Tasks.Task or a System.Threading.Tasks.Task<TResult>, based on whether the corresponding synchronous method returns void or a type TResult.

TAP 方法的参数应与其同步副本的参数匹配,并应以相同顺序提供。The parameters of a TAP method should match the parameters of its synchronous counterpart, and should be provided in the same order. 但是,outref 参数不受此规则的限制,并应完全避免。However, out and ref parameters are exempt from this rule and should be avoided entirely. 应该将通过 outref 参数返回的所有数据改为作为由 TResult 返回的 Task<TResult> 的一部分返回,且应使用元组或自定义数据结构来容纳多个值。Any data that would have been returned through an out or ref parameter should instead be returned as part of the TResult returned by Task<TResult>, and should use a tuple or a custom data structure to accommodate multiple values.

专用于创建、控制或组合任务的方法无需遵循此命名模式,因为方法名称或方法所属类型的名称已明确指明方法的异步用途;此类方法通常称为“组合器”。Methods that are devoted exclusively to the creation, manipulation, or combination of tasks (where the asynchronous intent of the method is clear in the method name or in the name of the type to which the method belongs) need not follow this naming pattern; such methods are often referred to as combinators. 组合器的示例包括 WhenAllWhenAny使用基于任务的异步模式一文的使用基于任务的内置组合器部分对此进行了介绍。Examples of combinators include WhenAll and WhenAny, and are discussed in the Using the Built-in Task-based Combinators section of the article Consuming the Task-based Asynchronous Pattern.

有关展示了 TAP 语法与旧异步编程模式(如异步编程模型 (APM) 和基于事件的异步模式 (EAP))语法区别的示例,请参阅异步编程模式For examples of how the TAP syntax differs from the syntax used in legacy asynchronous programming patterns such as the Asynchronous Programming Model (APM) and the Event-based Asynchronous Pattern (EAP), see Asynchronous Programming Patterns.

初始化异步操作Initiating an Asynchronous Operation

基于 TAP 的异步方法可以同步完成少量工作,如在返回结果任务之前,验证自变量和启动异步操作。An asynchronous method that is based on TAP can do a small amount of work synchronously, such as validating arguments and initiating the asynchronous operation, before it returns the resulting task. 应将同步工作保持最小,以便异步方法可以快速返回。Synchronous work should be kept to the minimum so the asynchronous method can return quickly. 快速返回的原因包括以下几种:Reasons for a quick return include the following:

  • 可以从用户界面 (UI) 线程调用异步方法,因此,所有长期运行的同步工作可能会降低应用程序的响应能力。Asynchronous methods may be invoked from user interface (UI) threads, and any long-running synchronous work could harm the responsiveness of the application.

  • 可以同时启动多个异步方法。Multiple asynchronous methods may be launched concurrently. 因此,在异步方法的同步部分中的任何长时间运行的工作都可以延迟其他异步操作的启动,从而减少并发的优点。Therefore, any long-running work in the synchronous portion of an asynchronous method could delay the initiation of other asynchronous operations, thereby decreasing the benefits of concurrency.

在某些情况下,完成操作所需的工作量要比异步启动操作所需的工作量少。In some cases, the amount of work required to complete the operation is less than the amount of work required to launch the operation asynchronously. 读取流时,按照在内存中已缓冲好的数据来满足该读取,这就是此类情形的一个示例。Reading from a stream where the read operation can be satisfied by data that is already buffered in memory is an example of such a scenario. 在这样的情况下,操作可能会同步完成,同时返回已完成的任务。In such cases, the operation may complete synchronously, and may return a task that has already been completed.

异常Exceptions

异步方法应引发仅将引发异步方法调用的异常,以响应用法错误。An asynchronous method should raise an exception to be thrown out of the asynchronous method call only in response to a usage error. 用法错误决不应该出现在成品代码中。Usage errors should never occur in production code. 例如,如果将 null 引用(在 Visual Basic 中为 Nothing)作为某个方法的参数传递导致了错误状态(通常由 ArgumentNullException 异常表示),则可以修改调用代码以确保绝对不传递 null 引用。For example, if passing a null reference (Nothing in Visual Basic) as one of the method’s arguments causes an error state (usually represented by an ArgumentNullException exception), you can modify the calling code to ensure that a null reference is never passed. 对于所有其他错误,在运行异步方法时发生的异常应分配给返回的任务,即使该异步方法碰巧在任务返回前同步完成。For all other errors, exceptions that occur when an asynchronous method is running should be assigned to the returned task, even if the asynchronous method happens to complete synchronously before the task is returned. 通常,任务最多包含一个异常。Typically, a task contains at most one exception. 但是,如果任务表示多个操作(例如,WhenAll),则多个异常可能与单个任务关联。However, if the task represents multiple operations (for example, WhenAll), multiple exceptions may be associated with a single task.

目标环境Target Environment

在实现 TAP 方法时,你可以确定异步执行发生的位置。When you implement a TAP method, you can determine where asynchronous execution occurs. 你可选择在线程池上执行工作负荷,可选择使用异步 I/O 实现它(不必绑定到大部分操作执行的线程)或可选择在特定线程(如 UI 线程)上运行它或使用任何数目的潜在上下文。You may choose to execute the workload on the thread pool, implement it by using asynchronous I/O (without being bound to a thread for the majority of the operation’s execution), run it on a specific thread (such as the UI thread), or use any number of potential contexts. TAP 方法甚至可能没有要执行的代码,可能只返回 Task 表示系统其他位置发生的情况(例如,表示到达已排入队列数据结构的数据的任务)。A TAP method may even have nothing to execute, and may just return a Task that represents the occurrence of a condition elsewhere in the system (for example, a task that represents data arriving at a queued data structure).

TAP 方法的调用方可能会同步等待生成的任务,以阻止等待 TAP 方法完成,也可能会在异步操作完成时运行其他(延续)代码。The caller of the TAP method may block waiting for the TAP method to complete by synchronously waiting on the resulting task, or may run additional (continuation) code when the asynchronous operation completes. 延续代码的创建者可以控制该代码的执行位置。The creator of the continuation code has control over where that code executes. 你可以通过 Task 类上的方法(例如,ContinueWith)显式创建延续代码,也可以使用基于延续(例如,C# 中的 await、Visual Basic 中的 Await 和 F# 中的 AwaitValue)构建的语言支持隐式创建延续代码。You may create the continuation code either explicitly, through methods on the Task class (for example, ContinueWith) or implicitly, by using language support built on top of continuations (for example, await in C#, Await in Visual Basic, AwaitValue in F#).

任务状态Task Status

Task 类提供了异步操作的生命周期,且该周期由 TaskStatus 枚举表示。The Task class provides a life cycle for asynchronous operations, and that cycle is represented by the TaskStatus enumeration. 为了支持派生自 TaskTask<TResult> 的类型的个别案例,并支持调度时分离构造,Task 类公开了 Start 方法。To support corner cases of types that derive from Task and Task<TResult>, and to support the separation of construction from scheduling, the Task class exposes a Start method. 公共 Task 构造函数创建的任务称为“冷任务”,因为它们在非计划 Created 状态下开始生命周期,并仅在对这些实例调用 Start 时才被排入计划。Tasks that are created by the public Task constructors are referred to as cold tasks, because they begin their life cycle in the non-scheduled Created state and are scheduled only when Start is called on these instances.

所有其他任务在热状态下开始其生命周期,这意味着它们表示的异步操作已启动,并且其任务状态是 TaskStatus.Created 以外的枚举值。All other tasks begin their life cycle in a hot state, which means that the asynchronous operations they represent have already been initiated and their task status is an enumeration value other than TaskStatus.Created. 必须激活从 TAP 方法返回的所有任务。All tasks that are returned from TAP methods must be activated. 如果 TAP 方法在内部使用任务的构造函数来实例化要返回的任务,TAP 方法必须在返回前先对 Task 对象调用 StartIf a TAP method internally uses a task’s constructor to instantiate the task to be returned, the TAP method must call Start on the Task object before returning it. TAP 方法的使用者可以安全地假设返回的任务处于活动状态且不应尝试对从 TAP 方法返回的任何 Start 调用 TaskConsumers of a TAP method may safely assume that the returned task is active and should not try to call Start on any Task that is returned from a TAP method. 对活动的任务调用 Start 将引发 InvalidOperationException 异常。Calling Start on an active task results in an InvalidOperationException exception.

取消(可选)Cancellation (Optional)

在 TAP 中,取消是异步方法实现者和异步方法使用者的选项。In TAP, cancellation is optional for both asynchronous method implementers and asynchronous method consumers. 如果操作允许取消,则会公开接受取消标记(CancellationToken 实例)的异步方法的重载。If an operation allows cancellation, it exposes an overload of the asynchronous method that accepts a cancellation token (CancellationToken instance). 按照约定,该参数命名为 cancellationTokenBy convention, the parameter is named cancellationToken.

public Task ReadAsync(byte [] buffer, int offset, int count, 
                      CancellationToken cancellationToken)
Public Function ReadAsync(buffer() As Byte, offset As Integer, 
                          count As Integer, 
                          cancellationToken As CancellationToken) _ 
                          As Task

该异步操作监视取消请求的此标记。The asynchronous operation monitors this token for cancellation requests. 如果它收到取消请求,则可以选择接受该请求并取消操作。If it receives a cancellation request, it may choose to honor that request and cancel the operation. 如果取消请求导致过早地结束工作,则 TAP 方法返回一个在 Canceled 状态下结束的任务;没有可用结果且不引发异常。If the cancellation request results in work being ended prematurely, the TAP method returns a task that ends in the Canceled state; there is no available result and no exception is thrown. Canceled 状态被视为任务的最终(完成)状态,以及 FaultedRanToCompletion 状态。The Canceled state is considered to be a final (completed) state for a task, along with the Faulted and RanToCompletion states. 因此,如果一个任务处于 Canceled 状态,则其 IsCompleted 属性将返回 trueTherefore, if a task is in the Canceled state, its IsCompleted property returns true. Canceled 状态下完成任务时,将计划或执行向任务注册的任何延续,除非延续选项(如 NotOnCanceled)特定于取消延续。When a task completes in the Canceled state, any continuations registered with the task are scheduled or executed, unless a continuation option such as NotOnCanceled was specified to opt out of continuation. 任何通过使用语言功能异步等待已取消的任务的代码都将继续运行,但不接收 OperationCanceledException 或其中派生的异常。Any code that is asynchronously waiting for a canceled task through use of language features continues to run but receives an OperationCanceledException or an exception derived from it. 通过诸如 Wait 的方法同步阻止的代码等待任务,并且 WaitAll 将继续运行但出现异常。Code that is blocked synchronously waiting on the task through methods such as Wait and WaitAll also continue to run with an exception.

如果取消标记请求在接受调用标记的 TAP 方法之前取消,TAP 方法应返回 Canceled 任务。If a cancellation token has requested cancellation before the TAP method that accepts that token is called, the TAP method should return a Canceled task. 但是,如果在运行异步操作时请求取消,则异步操作不需要接受该取消请求。However, if cancellation is requested while the asynchronous operation is running, the asynchronous operation need not accept the cancellation request. 仅当该操作如取消请求的结果那样结束时,返回的任务才应以 Canceled 状态结束。The returned task should end in the Canceled state only if the operation ends as a result of the cancellation request. 如果已请求取消,但仍然生成了结果或异常,则任务应在 RanToCompletionFaulted 状态下结束。If cancellation is requested but a result or an exception is still produced, the task should end in the RanToCompletion or Faulted state.

对于要首先对其公开可取消功能的异步方法,无需提供不接受取消令牌的重载。For asynchronous methods that want to expose the ability to be cancelled first and foremost, you don't have to provide an overload that doesn’t accept a cancellation token. 对于无法取消的方法,不提供接受取消标记的重载;这有助于向调用方指示目标方法是否真正可取消。For methods that cannot be canceled, do not provide overloads that accept a cancellation token; this helps indicate to the caller whether the target method is actually cancelable. 不要求取消的使用者代码可以调用接受 CancellationToken 的方法,并提供 None 作为参数值。Consumer code that does not desire cancellation may call a method that accepts a CancellationToken and provide None as the argument value. None 在功能上等效于默认 CancellationTokenNone is functionally equivalent to the default CancellationToken.

进度报告(可选)Progress Reporting (Optional)

某些异步操作受益于提供进度通知;这些通常用于使用有关该异步操作的进度的信息更新用户界面。Some asynchronous operations benefit from providing progress notifications; these are typically used to update a user interface with information about the progress of the asynchronous operation.

在 TAP 中,通过 IProgress<T> 接口处理进度,此接口作为通常名为 progress 的参数传递给异步方法。In TAP, progress is handled through an IProgress<T> interface, which is passed to the asynchronous method as a parameter that is usually named progress. 调用异步方法时提供进度接口有助于消除不正确使用导致的争用情况(也就是说,操作启动后未正确注册的事件处理程序可能缺少更新)。Providing the progress interface when the asynchronous method is called helps eliminate race conditions that result from incorrect usage (that is, when event handlers that are incorrectly registered after the operation starts may miss updates). 更重要的是,根据所使用的代码,进度接口将支持不同的进度实现。More importantly, the progress interface supports varying implementations of progress, as determined by the consuming code. 例如,使用代码可能只关心最新的进度更新,可能需要缓冲所有更新,可能需要为各个更新调用操作,也可能需要控制是否需将该调用封送到特定线程。For example, the consuming code may only care about the latest progress update, or may want to buffer all updates, or may want to invoke an action for each update, or may want to control whether the invocation is marshaled to a particular thread. 通过使用接口的不同实现,所有这些选项都可以满足特定的使用者的需要。All these options may be achieved by using a different implementation of the interface, customized to the particular consumer’s needs. 与取消一样,仅在 API 支持进度通知时,TAP 实现才应提供 IProgress<T> 参数。As with cancellation, TAP implementations should provide an IProgress<T> parameter only if the API supports progress notifications.

例如,如果本文前面所述的 ReadAsync 方法可以以到目前为止读取的字节数的形式报告中间进度,则进度回调可能为 IProgress<T> 接口:For example, if the ReadAsync method discussed earlier in this article is able to report intermediate progress in the form of the number of bytes read thus far, the progress callback could be an IProgress<T> interface:

public Task ReadAsync(byte[] buffer, int offset, int count, 
                      IProgress<long> progress)
Public Function ReadAsync(buffer() As Byte, offset As Integer, 
                          count As Integer, 
                          progress As IProgress(Of Long)) As Task 

如果 FindFilesAsync 方法返回满足特定搜索模式的所有文件的列表,则进度回调可以对已完成工作的百分比和当前部分结果集进行估计。If a FindFilesAsync method returns a list of all files that meet a particular search pattern, the progress callback could provide an estimate of the percentage of work completed as well as the current set of partial results. 可使用元组执行此操作:It could do this either with a tuple:

public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
            string pattern, 
            IProgress<Tuple<double, 
            ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(pattern As String, 
                               progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) _
                               As  Task(Of ReadOnlyCollection(Of FileInfo))

也可以使用特定于 API 的数据类型执行此操作:or with a data type that is specific to the API:

public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
    string pattern, 
    IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(pattern As String, 
                               progress As IProgress(Of FindFilesProgressInfo)) _
                               As Task(Of ReadOnlyCollection(Of FileInfo))

在后一种情况下,特殊数据类型应加上后缀 ProgressInfoIn the latter case, the special data type is usually suffixed with ProgressInfo.

如果 TAP 实现提供接受 progress 参数的重载,则必须允许该参数成为 null,在这种情况下,不会报告任何进度。If TAP implementations provide overloads that accept a progress parameter, they must allow the argument to be null, in which case no progress will be reported. TAP 实现应该同步将进度报告到 Progress<T> 对象,使异步方法能够快速提供进度,并允许进度的使用者确定处理信息的最佳方式和位置。TAP implementations should report the progress to the Progress<T> object synchronously, which enables the asynchronous method to quickly provide progress, and allow the consumer of the progress to determine how and where best to handle the information. 例如,进度实例可以选择将回调封送,并引发有关捕获到的同步上下文的事件。For example, the progress instance could choose to marshal callbacks and raise events on a captured synchronization context.

IProgress<T> 实现IProgress<T> Implementations

.NET Framework 4.5.NET Framework 4.5 提供单个 IProgress<T> 实现:Progress<T>The .NET Framework 4.5.NET Framework 4.5 provides a single IProgress<T> implementation: Progress<T>. Progress<T> 类的声明方式如下:The Progress<T> class is declared as follows:

public class Progress<T> : IProgress<T>  
{  
    public Progress();  
    public Progress(Action<T> handler);  
    protected virtual void OnReport(T value);  
    public event EventHandler<T> ProgressChanged;  
}  
Public Class Progress(Of T) : Inherits IProgress(Of T)  
    Public Sub New()  
    Public Sub New(handler As Action(Of T))  
    Protected Overridable Sub OnReport(value As T)  
    Public Event ProgressChanged As EventHandler(Of T>  
End Class  

Progress<T> 的实例公开 ProgressChanged 事件,此事件在异步操作每次报告进度更新时引发。An instance of Progress<T> exposes a ProgressChanged event, which is raised every time the asynchronous operation reports a progress update. 实例化 ProgressChanged 实例后,会在捕获到的 SynchronizationContext 对象上引发 Progress<T> 事件。The ProgressChanged event is raised on the SynchronizationContext object that was captured when the Progress<T> instance was instantiated. 如果没有可用的同步上下文,则使用针对线程池的默认上下文。If no synchronization context was available, a default context that targets the thread pool is used. 可以向此事件注册处理程序。Handlers may be registered with this event. 为了方便起见,也可将单个处理程序提供给 Progress<T> 构造函数,并且行为与 ProgressChanged 事件的事件处理程序一样。A single handler may also be provided to the Progress<T> constructor for convenience, and behaves just like an event handler for the ProgressChanged event. 异步引发进度更新以避免延迟异步操作,同时执行事件处理程序。Progress updates are raised asynchronously to avoid delaying the asynchronous operation while event handlers are executing. 另一个 IProgress<T> 实现可以选择应用不同的语义。Another IProgress<T> implementation could choose to apply different semantics.

选择要提供的重载Choosing the Overloads to Provide

如果 TAP 实现使用可选的 CancellationToken 和可选的 IProgress<T> 参数,则可能需要多达四次的重载:If a TAP implementation uses both the optional CancellationToken and optional IProgress<T> parameters, it could potentially require up to four overloads:

public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, CancellationToken cancellationToken);  
public Task MethodNameAsync(…, IProgress<T> progress);   
public Task MethodNameAsync(…,   
    CancellationToken cancellationToken, IProgress<T> progress);  
Public MethodNameAsync(…) As Task  
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task  
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task   
Public MethodNameAsync(…, cancellationToken As CancellationToken,   
                       progress As IProgress(Of T)) As Task  

但是,许多 TAP 实现没有提供取消或进度功能,因此它们需要一个方法:However, many TAP implementations provide neither cancellation or progress capabilities, so they require a single method:

public Task MethodNameAsync(…);  
Public MethodNameAsync(…) As Task  

如果 TAP 实现支持取消或进度但不同时支持二者,则 TAP 实现可能提供以下两种重载:If a TAP implementation supports either cancellation or progress but not both, it may provide two overloads:

public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, CancellationToken cancellationToken);  

// … or …  

public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, IProgress<T> progress);  
Public MethodNameAsync(…) As Task  
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task  

' … or …  

Public MethodNameAsync(…) As Task  
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task  

如果 TAP 实现同时支持取消和进度,则会公开所有四个重载。If a TAP implementation supports both cancellation and progress, it may expose all four overloads. 但是,它只提供以下两个:However, it may provide only the following two:

public Task MethodNameAsync(…);  
public Task MethodNameAsync(…,   
    CancellationToken cancellationToken, IProgress<T> progress);  
Public MethodNameAsync(…) As Task  
Public MethodNameAsync(…, cancellationToken As CancellationToken,   
                       progress As IProgress(Of T)) As Task  

若要弥补缺少的两个中间组合,开发人员可以传递 None 参数的 CancellationToken 或默认的 cancellationTokennull 参数的 progressTo compensate for the two missing intermediate combinations, developers may pass None or a default CancellationToken for the cancellationToken parameter and null for the progress parameter.

如果需要 TAP 方法的每种用法支持取消或进度,则可以忽略不接受相关参数的重载。If you expect every usage of the TAP method to support cancellation or progress, you may omit the overloads that don’t accept the relevant parameter.

如果决定公开多个重载以使取消或进度可选,则不支持取消或进度的重载的行为方式就像其已将取消的 None 或进度的 null 传递给确实支持它们的重载。If you decide to expose multiple overloads to make cancellation or progress optional, the overloads that don’t support cancellation or progress should behave as if they passed None for cancellation or null for progress to the overload that does support these.

标题Title 描述Description
异步编程模式Asynchronous Programming Patterns 介绍执行异步操作的三种模式:基于任务的异步模式 (TAP)、异步编程模型 (APM) 和基于事件的异步模式 (EAP)。Introduces the three patterns for performing asynchronous operations: the Task-based Asynchronous Pattern (TAP), the Asynchronous Programming Model (APM), and the Event-based Asynchronous Pattern (EAP).
实现基于任务的异步模式Implementing the Task-based Asynchronous Pattern 描述如何使用以下三种方式实现基于任务的异步模式 (TAP):手动使用 Visual Studio 中的 C# 和 Visual Basic 编译器,或通过编译器和手动方法的组合。Describes how to implement the Task-based Asynchronous Pattern (TAP) in three ways: by using the C# and Visual Basic compilers in Visual Studio, manually, or through a combination of the compiler and manual methods.
使用基于任务的异步模式Consuming the Task-based Asynchronous Pattern 描述你可以如何使用任务和回调实现等待,而无需阻止。Describes how you can use tasks and callbacks to achieve waiting without blocking.
与其他异步模式和类型互操作Interop with Other Asynchronous Patterns and Types 描述如何使用基于任务的异步模式 (TAP) 实现异步编程模型 (APM) 和基于事件的异步模式 (EAP)。Describes how to use the Task-based Asynchronous Pattern (TAP) to implement the Asynchronous Programming Model (APM) and Event-based Asynchronous Pattern (EAP).