同步和异步操作

描述本地异步实现和调用以及对异步消息交换的同步使用。

许多应用程序都以异步方式调用方法,因为这样允许应用程序在运行方法调用时继续执行有用的工作。Windows Communication Foundation (WCF) 服务和客户端可以在两个不同的应用程序级别参与异步操作调用,这会为 WCF 应用程序在最大化针对交互性而平衡的吞吐量方面提供更好的灵活性。

异步操作的类型

无论参数类型和返回值如何,WCF 中的所有服务协定都使用 WCF 属性来指定客户端和服务之间的特殊消息交换模式。WCF 自动将入站和出站消息路由到相应的服务操作或正在运行的客户端代码。

客户端仅拥有为特殊操作指定消息交换模式的服务协定。客户端可以为开发人员提供他们所选择的任何编程模型,但前提是找到了基础消息交换模式。因此,只要找到了指定的消息模式,服务也可以按任何方式来实现操作。

如果服务协定独立于服务或客户端实现,则可以在 WCF 应用程序中实现下列形式的异步执行:

  • 客户端可以使用同步消息交换以异步方式调用请求/响应操作。
  • 服务可以使用同步消息交换以异步方式实现请求/响应操作。
  • 消息交换可以是单向的,而与客户端或服务的实现无关。

建议的异步方案

如果操作服务实现执行阻止调用(如执行 I/O 工作),则在该服务操作实现中使用异步方法。在异步操作实现中,可尝试调用异步操作和方法来尽可能远地扩展异步调用路径。例如,从 BeginOperationOne() 中调用 BeginOperationTwo()

  • 在下列情况下,可在客户端应用程序或调用应用程序中使用异步方法:
  • 如果从中间层应用程序调用操作。(有关 这类方案的更多信息,请参见中间层客户端应用程序。)
  • 如果在 ASP.NET 页中调用操作,可使用异步页。
  • 如果从任何单线程的应用程序(如 Windows 窗体或 Windows Presentation Foundation (WPF))调用操作。使用基于事件的异步调用模型时,将在 UI 线程上引发结果事件,从而向应用程序添加响应性而无需您自己处理多个线程。
  • 通常,如果可在同步调用和异步调用之间进行选择,应选择异步调用。

客户端异步调用

WCF 客户端应用程序可使用两个异步调用模型之一,Asynchronous Programming Design Patterns中介绍了这两个异步调用模型:

第一种方法,基于事件的异步模式,建议用于调用应用程序,因为它只需添加事件处理程序,即可接收响应的通知 -- 将在用户界面线程上自动引发生成的事件。若要使用此方法,请使用 ServiceModel Metadata Utility Tool (Svcutil.exe) 并同时指定 /async/tcv:Version35 命令选项,如下面的示例所示。

svcutil https://localhost:8000/servicemodelsamples/service/mex /async /tcv:Version35

完成此操作后,Svcutil.exe 将生成带有事件基础结构的 WCF 客户端类,该事件基础结构使调用应用程序可以实现和分配事件处理程序,以便接收响应和采取相应的操作。有关完整示例,请参见 如何:以异步方式调用 WCF 服务操作

但是,基于事件的异步模型仅在 .NET Framework 3.5 版 中可用。此外,如果 WCF 客户端通道是使用 System.ServiceModel.ChannelFactory 创建的,那么即使在 .NET Framework 3.5 中,也不支持该模型。使用 WCF 客户端通道对象时,必须使用 System.IAsyncResult 对象异步调用操作。若要使用此方法,请使用 ServiceModel Metadata Utility Tool (Svcutil.exe) 并同时指定 /async 命令选项,如下面的示例所示。

svcutil https://localhost:8000/servicemodelsamples/service/mex /async 

这会生成一个服务协定,在该服务协定中,每个操作都作为 <Begin> 方法(其 AsyncPattern 属性设置为 true)和相应的 <End> 方法来建模。有关使用 ChannelFactory 的完整示例,请参见如何:使用通道工厂以异步方式调用操作

在任何一种情况下,即使服务是以同步方式实现的,应用程序也可以异步方式调用操作,就如同应用程序可以使用同一个模式以异步方式调用本地同步方法一样。操作的实现方式对于客户端而言无关紧要;当响应消息到达时,其内容会调度到客户端的异步 <End> 方法,并且客户端会检索相关信息。

同步操作实现

同样,服务操作也可以按异步方式实现,具体方法是使用 .NET Framework 异步编程模式,并标记 AsyncPattern 属性设置为 true<Begin> 方法。在这种情况下,异步操作将以与同步操作相同的方式在元数据中公开,即作为单个操作随请求消息和相关的响应消息来公开。随后,客户端编程模型可以进行选择。客户端编程模型可以将这种模式表示为同步操作,也可以表示为异步操作,但前提是调用该服务时发生了请求-响应消息交换。

有关示例,请参见如何:实现异步服务操作

定义一个异步执行(而不考虑它在客户端应用程序中的调用方式)的协定操作 X

  • 使用 BeginOperationEndOperation 模式定义两个方法。
  • BeginOperation 方法包括该操作的 inout 参数,并返回一个 IAsyncResult 类型。
  • EndOperation 方法包括一个 IAsyncResult 参数以及 inout 参数,并返回操作的返回类型。

有关示例,请参见下面的方法。

int DoWork(string data, ref string inout, out string outonly)
Function DoWork(ByVal data As String, ByRef inout As String, _
out outonly As out) As Integer

若要创建异步操作,这两种方法应为:

[OperationContract(AsyncPattern=true)]
IAsyncResult BeginDoWork(string data, 
                          ref string inout, 
                          AsyncCallback callback, 
                          object state);
int EndDoWork(ref string inout, out string outonly, IAsyncResult result);
<OperationContract(AsyncPattern := True)>  _
Function BeginDoWork(ByVal data As String, _
                 ByRef inout As String, _
                 ByVal callback As AsyncCallback, _
                 ByVal state As Object) _
As IAsyncResult 

Function EndDoWork(ByRef inout As String, _
        ByRef outonly As String, _
        ByVal result As IAsyncResult) _
As Integer

提示

OperationContractAttribute 属性仅适用于 BeginDoWork 方法。所得到的协定具有一个名为 DoWork 的 WSDL 操作。

单向消息交换模式

还可以创建一个异步消息交换模式,在该模式下,单向操作(其 System.ServiceModel.OperationContractAttribute.IsOneWaytrue 的操作,该操作没有相关的响应)可以由客户端或服务独立于另一端来单向发送。(这会将双工消息交换模式用于单向消息。)在这种情况下,服务协定指定单向消息交换,即,任一端均可根据相应的情况以(或者不以)异步调用/实现形式来实现。通常,当服务协定是单向消息交换时,这些实现极有可能是同步的,因为在发送消息之后,应用程序不会等待答复,而是继续执行其他工作。

基于事件的异步模式客户端和消息协定

基于事件的异步模型设计准则规定,如果返回了多个值,则一个值会作为 Result 属性返回,其他值会作为 EventArgs 对象上的属性返回。因此产生的结果之一是,如果客户端使用基于事件的异步命令选项导入元数据,且该操作返回多个值,则默认的 EventArgs 对象将返回一个值作为 Result 属性,返回的其余值是 EventArgs 对象的属性。

如果要将消息对象作为 Result 属性来接收并要使返回的值作为该对象上的属性,请使用 /messageContract 命令选项。这会生成一个签名,该签名会将响应消息作为 EventArgs 对象上的 Result 属性返回。然后,所有内部返回值就都是响应消息对象的属性了。

另请参见

参考

IsOneWay
AsyncPattern