(系列博文源自 http://pfelix.wordpress.com/,由笔者翻译并整理,转载请注明)
在wcf 的 service contract中, 服务接口方法定义都要求有 operation context这个标签。 在这个标签里有一个 AsyncPattern的属性用以定制 service的异步模型。
考虑如下接口定义:
1: [ServiceContract]
2: interface IService {
3: [OperationContract]
4: ReplyData Operation1(RequestData rd);
5:
6: [OperationContract(AsyncPattern = true)]
7: IAsyncResult BeginOperation2(RequestData rd, AsyncCallback ac, object obj);
8: ReplyData EndOperation2(IAsyncResult ar);
9: }
- 第一个服务方法定义中, AsyncPattern默认为false。 这是一个简单的 request-response方法 'Operation1'. 在默认设置下,这个方法由server端同步实现。 具体过程是由 WCF Dispatcher调用这个方法,生成传入参数,在这个方法执行过程中会占用这个service线程,最终返回reply,并由wcf生成返回消息传回客户端。
- 第二个服务方法定义中, AsyncPattern设成true,同样定义了一个request-response方法 'Operation2'。然而, 这个方法实现要求是一个包含两个方法:BeginOperation2, EndOperation2的异步实现。在这里的‘异步’ 意味着
- WCF dispatcher 调用 BeginOperation2 , 生成传入参数和一个异步回调, 还有一些状态信息。
- 这个方法开始执行并最终返回一个IAsyncResult 。
- 当这个方法执行结束时会调用AsyncCallback 。
- 这个AsyncCallback 是由wcf实现 会接下来调用 EndOperation2 收集返回值并把消息传回给客户端。
需要注意的是,即使设置了AsyncPattern, 在通信层面而言这个service依然是一个 request-response 模型。 这个异步只是service 的实现细节。 参考生成的wsdl定义:
1: <wsdl:portType name="IService"> 2: 3: <wsdl:operation name="Operation1"> 4: <wsdl:input wsaw:Action="..." message="..." /> 5: <wsdl:output wsaw:Action="..." message="..." /> 6: </wsdl:operation> 7: 8: <wsdl:operation name="Operation2"> 9: <wsdl:input wsaw:Action="..." message="..." /> 10: <wsdl:output wsaw:Action="..." message="..." /> 11: </wsdl:operation> 12: 13: </wsdl:portType>
这并不会改变service的通信模型,有别于普遍意义上的 “异步服务”。
那么,这种实现方式有什么意义? 或者说在何种场景下应该使用这种实现方式?
砖家说,当服务实现中会长时间block住当前服务线程时就应该考虑这种实现方式。
结合上图应该能比较容易理解这种方式带来的优势, 在上图的例子中这种实现方式能在开始 io前释放服务线程, 并在 io结束后重新获得一个线程做后续操作。
理论上来说这种方式就不会存在一个‘被阻塞’的线程,以节约线程资源来提升service并发。
最后提一下在client端这个属性的作用。
当使用 svcutil.exe时加上 /async 会生成以下服务器代理接口定义:
1: public interface IService {
2:
3: // Operation 1 synchronous method
4: [System.ServiceModel.OperationContractAttribute(Action=".../Operation1", ReplyAction=".../Operation1Response")]
5: ReplyData Operation1(RequestData rd);
6:
7: // Operation 1 asynchronous method pair
8: [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action=".../Operation1", ReplyAction=".../Operation1Response")]
9: System.IAsyncResult BeginOperation1(RequestData rd, System.AsyncCallback callback, object asyncState);
10: ReplyData EndOperation1(System.IAsyncResult result); 11:
12: // Operation 2 synchronous method
13: [System.ServiceModel.OperationContractAttribute(Action=".../Operation2", ReplyAction=".../Operation2Response")]
14: ReplyData Operation2(AsyncPattern.Client.AsyncClient.RequestData rd);
15:
16: // Operation 2 asynchronous method pair
17: [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action=".../Operation2", ReplyAction=".../Operation2Response")]
18: System.IAsyncResult BeginOperation2(RequestData rd, System.AsyncCallback callback, object asyncState);
19: ReplyData EndOperation2(System.IAsyncResult result); 20:
21: }
如果理解了一开始的部分,很容易想到这根服务器端的实现方式无关。 这只是客户端的一种内部实现方式。
其效果就是调用服务器时会立刻返回(而不阻塞),再由调用EndOperation的形式得到执行结果。
值得一提的是类似的效果虽然可以通过多线程来实现。 但依然不会有wcf Async机制高效, 因为wcf 的异步模型是基于通信层支持, 并不会阻塞任何一个线程。而多线程实现依然会阻塞线程池中的异步线程。
(译者:多数情况而言在客户端这点优势倒没啥意义。。。。)