使用 .NET 异步编程,在程序继续执行的同时对 .NET 类方法进行调用,直到进行指定的回调为止;或者如果没有提供回调,则直到对调用的阻塞、轮询或等待完成为止。例如,一个程序可以调用一个方法,该方法枚举一个较大的列表,同时主程序将继续执行。在完成枚举后,进行回调并且程序对它进行寻址。
异步编程是由 .NET 框架的许多区域支持的功能,这些区域包括:
文件 IO、流 IO、套接字 IO
网络:HTTP、TCP
远程处理信道(HTTP、TCP)和代理
使用 ASP.NET 创建的 XML Web services
ASP.NET Web 窗体
MSMQ 上的对消息处理消息队列
异步委托
异步编程概述
可以采用以下四种方法中的一种生成和完成 .NET 异步调用:
使用回调
在开始异步调用时提供回调委托。
轮询完成
为调用完成轮询已返回的 IAsyncResult.IsCompleted 属性。
开始调用,结束调用
尝试过早完成操作,因而在操作完成前阻塞。
开始调用,等待处理,结束调用
等候 IAsyncResult。此选项与前面选项的区别在于:客户端可以使用超时定期唤醒。
通过 .NET 异步编程,服务器将异步操作拆分成两个逻辑部分:采用来自客户端的输入并开始异步操作的部分,向客户端提供异步操作的结果的部分。此外,对于异步操作所需的输入,第一部分还采用在异步操作完成后将被调用的 AsyncCallback 委托。第一部分返回一个可等待的对象,该对象实现客户端所使用的 IAsyncResult 接口来确定异步操作的状态。服务器通常利用第一部分返回给客户端的可等待的对象以维持与异步操作关联的任何状态。通过提供可等待的对象,客户端使用第二部分获取异步操作的结果。
取消
在 IAsyncResult 接口上不提供 Cancel。在 IasyncResult 上不提供 Cancel,因为在许多实现中,可能没有对取消 BeginXXX 调用的保证。公开和实现对异步 BeginXXX 调用的取消是由类的实施者在其公开 BeginXXX 和 EndXXX 时执行的。您还可以选择提供 CancelXXX 方法。
异常
BeginXXX。如果 BeginXXX 方法引发一个异常,则调用方可以假定异步操作未开始,并且将不调用回调委托。BeginXXX 可以引发结构异常。
EndXXX。通过从 EndXXX 方法引发异常来从异步操作返回异常。EndXXX 可以引发结构和被调用的对象异常。
异步编程设计模式
异步模式是 .NET 框架中的核心技术。它提供有效的模型来管理异步操作和一致的编程模型。在您已了解如何将异步模式与 IO 类一起使用后,可以很容易地将异步模式与网络类、委托等(包括新 API)一起使用。
异步设计模式概述
异步模式所提供的革新之一就是调用方确定特定调用是否应是异步的。对于被调用的对象,没有必要执行附加的编程来用于支持其客户端的异步行为;在该模式中异步委托提供此功能。公共语言运行库处理调用方和被调用的对象视图之间的差异。被调用的对象可以选择显式支持异步行为,这或者是因为它可以比一般结构更为有效地实现异步行为,或者是因为它想只支持其调用方的异步行为。但是,建议这种被调用的对象遵循公开异步操作的异步设计模式。
类型安全是异步模式的另一项革新。尤其对于异步委托,针对 .NET 框架和公共语言运行库的语言编译器可令映射到规则 Invoke 方法的开始和结束操作(例如,BeginInvoke 和 EndInvoke)的方法签名是类型安全的。这是十分重要的,因为编译器为异步委托将同步调用拆分成开始和结束操作,使其能够只传递有效参数。
在此模式中所蕴含的基本想法如下所示:
调用方确定特定调用是否应是异步的。
对于被调用的对象,没有必要由其客户端执行附加的编程来用于支持异步行为。公共语言运行库结构应该能够处理调用方和被调用的对象视图之间的差异。
被调用的对象可以选择显式支持异步行为,这或者是因为它可以比一般结构更为有效地实现异步行为,或者是因为它想只支持其调用方的异步行为。但是,建议这种被调用的对象遵循公开异步操作的异步设计模式。
编译器为 BeginInvoke 和 EndInvoke 以及异步委托生成类型安全方法签名。
.NET 框架提供支持异步编程模型所需的服务。此类服务的部分列表示例是:
同步基元,例如监视器和阅读器编写器锁定。
线程和线程池。
同步构造,例如支持等候对象的容器。
向基础结构片(例如 IMessage 对象和线程池)公开。
该模式将一个同步调用拆分成各构成部分:开始操作、结束操作和结果对象。考虑以下示例,在其中可能要用大量时间来完成 Factorize 方法。
public class PrimeFactorizer
{
public bool Factorize(int factorizableNum, ref int primefactor1, ref int primefactor2)
{
// Determine whether factorizableNum is prime.
// If is prime, return true. Otherwise, return false.
// If is prime, place factors in primefactor1 and primefactor2.
}
}
如果遵循异步模式,则类库编写器添加 BeginFactorize 和 EndFactorize 方法,这两个方法将同步操作拆分成两个异步操作:
public class PrimeFactorizer
{
public bool Factorize(
int factorizableNum,
ref int primefactor1,
ref int primefactor2)
{
// Determine whether factorizableNum is prime.
// if is prime, return true; otherwise return false.
// if is prime palce factors in primefactor1 and primefactor2
}
public IAsyncResult BeginFactorize(
int factorizableNum,
ref int primefactor1,
ref int primefactor2,
AsyncCallback callback,
Object state)
{
// Begin the factorizing asynchronously, and return a result object,
}
public bool EndFactorize(
ref int primefactor1,
ref int primefactor2,
IAsyncResult asyncResult
)
{
// End (or complete) the factorizing, and
// return the results,
// and obtain the prime factors.
}
}
服务器将异步操作拆分成两个逻辑部分:采用来自客户端的输入并调用异步操作的部分,向客户端提供异步操作结果的部分。
除了异步操作所需的输入外,第一部分还采用在完成异步操作时后要被调用的 AsyncCallback 委托。第一部分返回一个可等待的对象,该对象实现客户端使用的 IAsyncResult 接口来确定异步操作的状态。
服务器还利用它返回到客户端的可等待的对象来维护与异步操作关联的任何状态。通过提供可等待的对象,客户端使用第二部分获取异步操作的结果。
可用于客户端来启动异步操作的选项有:
在开始异步调用时提供回调委托。
public class Driver1
{
public PrimeFactorizer primeFactorizer;
public void Results(IAsyncResult asyncResult)
{
int primefactor1=0;
int primefactor2=0;
bool prime = primeFactorizer.EndFactorize(
ref primefactor1,
ref primefactor2,
asyncResult);
}
public void Work()
{
int factorizableNum=1000589023,
int primefactor1=0;
int primefactor2=0;
Object state = new Object();
primeFactorizer = new PrimeFactorizer();
AsyncCallback callback = new Callback(this.Results);
IAsyncResult asyncResult = primeFactorizer.BeginFactorize(
factorizableNum,
ref primefactor1,
ref primefactor2,
callback,
state);
}
}
在开始异步调用时不提供回调委托。
public class Driver2
{
public static void Work()
{
int factorizableNum=1000589023,
int primefactor1=0;
int primefactor2=0;
Object state = new Object();
PrimeFactorizer primeFactorizer = new PrimeFactorizer();
AsyncCallback callback = new Callback(this.Results);
IAsyncResult asyncResult = primeFactorizer.BeginFactorize(
factorizableNum,
ref primefactor1,
ref primefactor2,
callback,
state);
bool prime = primeFactorizer.EndFactorize(
ref primefactor1,
ref primefactor2,
asyncResult);
}
}
异步方法签名
方法签名是参数类型在一个方法中的顺序。BeginInvoke 异步方法签名的规则是:
包括所有 IN 参数。
包括所有 OUT 参数。
包括所有 IN/OUT 参数。
包括所有 ByRef 参数。
将 AsyncCallback 和 AsyncState(可通过 IAsyncResult 接口上的 AsyncState 属性获得)作为最后两个参数。
返回 IAsyncResult。
EndInvoke 异步方法签名的规则是:
包括所有 IN/OUT 参数。
包括所有 OUT 参数。
包括所有 ByRef 参数。
将 IAsyncResult 作为最后一个参数。
从原始方法签名返回原始返回类型。
结果对象 (IAsyncResult) 是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。结果对象被传递到结束操作,该操作返回调用的最终返回值。在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。
IAsyncResult 接口
IAsyncResult 接口用于监视和管理异步操作。该接口是从开始操作返回的并被传递到结束操作,以将开始操作和结束操作相关联。如果回调被指定为开始操作的一部分,则 AsyncResult 被传递到回调。以下代码阐释有关 IAsyncResult 接口的属性,该接口可用于监视异步操作的状态并获取还可被传递到开始操作中的异步状态对象:
public interface IAsyncResult
{
Object AsyncState { get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; }
}
AsyncState
返回被作为最后一个参数(作为开始操作方法调用的一部分)提供的对象。
AsyncWaitHandle
AsyncWaitHandle 属性返回 WaitHandle,它可用于执行 WaitHandle.WaitOne、WaitAny 或 WaitAll。
CompletedSynchronously
如果开始操作调用同步完成,则 CompletedSynchronously 属性将被设置为“true”。
IsCompleted
在服务器完成调用处理后,IsCompleted 属性将被设置为“true”。
用于异步操作的 AsyncCallback 委托
AsyncCallback 委托用于指定在开始操作完成后应被调用的方法。如以下代码中所示,AsyncCallback 委托被作为开始操作上的第二个到最后一个参数传递:
public delegate void AsyncCallback(IAsyncResult ar);