WCF技术剖析之十一:异步操作在WCF中的应用(上篇)
按照操作執(zhí)行所需的資源類(lèi)型,我們可以將操作分為CPU綁定型(CPU Bound)操作和I/O綁定型(I/O Bound)操作。對(duì)于前者,操作的執(zhí)行主要利用CPU進(jìn)行密集的計(jì)算,而對(duì)于后者,大部分的操作處理時(shí)間花在I/O操作處理,比如訪問(wèn)數(shù)據(jù)庫(kù)、文件系統(tǒng)、網(wǎng)絡(luò)資源等。對(duì)于I/O綁定型操作,我們可以充分利用多線程的機(jī)制,讓多個(gè)操作在自己的線程并發(fā)執(zhí)行,從而提高系統(tǒng)性能和響應(yīng)能力。服務(wù)調(diào)用就是典型的I/O綁定型操作,所以多線程在服務(wù)調(diào)用中具有廣泛的應(yīng)用。在本篇文章中,我們專(zhuān)門(mén)來(lái)討論多線程或者是異步操作在WCF中的具體應(yīng)用。
如果按照異步操作發(fā)生的位置,我個(gè)人將WCF應(yīng)用的異步操作分為下面3種變體。
- 異步信道調(diào)用:客戶(hù)端通過(guò)綁定創(chuàng)建的信道向服務(wù)端發(fā)送消息,從而實(shí)現(xiàn)了對(duì)服務(wù)的調(diào)用,不管消息通過(guò)信道向服務(wù)端發(fā)送的方式是同步的(采用請(qǐng)求-回復(fù)MEP進(jìn)行消息交換)還是異步的(采用單向MEP進(jìn)行消息交換),客戶(hù)端程序都可以通過(guò)代理對(duì)象異步地調(diào)用信道,從而實(shí)現(xiàn)異步服務(wù)調(diào)用;
- 單向(One-way)消息交換:客戶(hù)端的信道通過(guò)單向的消息交換模式向服務(wù)端發(fā)送消息,消息一旦抵達(dá)傳輸層馬上返回,從而達(dá)到異步服務(wù)調(diào)用的效果;
- 異步服務(wù)實(shí)現(xiàn):服務(wù)端在具體實(shí)現(xiàn)服務(wù)操作的時(shí)候,采用異步調(diào)用的方式。
圖1清晰地揭示了以上3種異步場(chǎng)景在整個(gè)服務(wù)調(diào)用中所發(fā)生的時(shí)機(jī)。對(duì)于這3種典型的異步操作,它們之間是相互獨(dú)立的。對(duì)于單向消息交換,由于在上面一節(jié)中已經(jīng)進(jìn)行過(guò)詳細(xì)的介紹,在本節(jié)中主要介紹其余兩種異步操作的具體使用。本篇文章我們著重探討第一種形式(異步信道調(diào)用)的異步調(diào)用,關(guān)于異步服務(wù)的實(shí)現(xiàn)放在下篇中。
圖1 WCF多線程應(yīng)用的三種典型場(chǎng)景
為了方便客戶(hù)端進(jìn)行異步的服務(wù)調(diào)用,最簡(jiǎn)便的方式就通過(guò)SvcUtil.exe這個(gè)代碼生成工具幫助我們生成機(jī)遇異步調(diào)用的服務(wù)代理類(lèi)。由于SvcUtil.exe同時(shí)也為VS提供了添加服務(wù)引用的實(shí)現(xiàn),異步服務(wù)代理也可以通過(guò)添加服務(wù)引用的方式創(chuàng)建。在具體通過(guò)服務(wù)代理進(jìn)行異步服務(wù)調(diào)用的時(shí)候,可以采用不同的調(diào)用形式,不僅可以采用參數(shù)典型的BeginXxx和EndXxx的形式,也可以采用回調(diào)(Callback)的形式,還可以采用事件注冊(cè)的形式。
一、異步服務(wù)代理的創(chuàng)建
對(duì)于任何一個(gè)服務(wù)操作,不管它是否采用了異步的實(shí)現(xiàn)方式,也不管是否采用單向的消息交換模式,我們均可以通過(guò)添加服務(wù)引用或者直接使用SvcUtil.exe的方式創(chuàng)建異步服務(wù)代理,對(duì)服務(wù)進(jìn)行異步調(diào)用。
如果通過(guò)添加服務(wù)引用的方式來(lái)創(chuàng)建異步服務(wù)代理,只需要在添加服務(wù)引用對(duì)話框中點(diǎn)擊“高級(jí)(Advanced)”按鈕,便會(huì)彈出如下一個(gè)“服務(wù)引用設(shè)置(Service Reference Settings)”對(duì)話框,勾選“生成異步操作(Generate asynchronous operations)”復(fù)選框即可,如圖2所示。
圖2 添加服務(wù)引用時(shí)生成異步操作的設(shè)置
通過(guò)這種方式生成的代理類(lèi)與沒(méi)有選擇“生成異步操作”選項(xiàng)一樣,都是生成一個(gè)繼承自ClientBase<TChannel>的類(lèi),所不同的是,該類(lèi)中會(huì)多出一些與異步服務(wù)調(diào)用相關(guān)的成員。我們同樣以我們的CalculatorService為例(服務(wù)契約的定義如下)。
1: [ServiceContract(Namespace="urn:artech.com")] 2: public interface ICalculator 3: { 4: [OperationContract] 5: double Add(double x, double y); 6: }通過(guò)這種方式生成的代理類(lèi)CalculateClient會(huì)多出下面列出的事件和方法成員。
1: public partial class CalculateClient : ClientBase< ICalculator>, ICalculator 2: { 3: //其他成員 4: public event System.EventHandler<AddCompleteEventArgs> AddComplete; 5: public IAsyncResult BeginAdd(double x, double y, AsyncCallback callback, object asyncState) 6: { 7: //省略實(shí)現(xiàn) 8: } 9: 10: public double EndAdd(System.IAsyncResult result) 11: { 12: //省略實(shí)現(xiàn) 13: } 14: 15: public void AddAsync(double x, double y) 16: { 17: //省略實(shí)現(xiàn) 18: } 19:? 20: public void AddAsync(double x, double y, object userState) 21: { 22: //省略實(shí)現(xiàn) 23: } 24: }事件AddComplete將在Add操作執(zhí)行之后觸發(fā),你可以注冊(cè)該事件,在運(yùn)算結(jié)束之后做一些特殊的工作,比如運(yùn)算結(jié)果的顯示。該事件包含一個(gè)特殊的EventArgs:AddCompleteEventArgs。該事件參數(shù)類(lèi)型同樣是通過(guò)添加服務(wù)引用自動(dòng)創(chuàng)建的。AddCompleteEventArgs繼承自System.ComponentModel.AsyncCompleteEventArgs。在事件處理器中可以通過(guò)該參數(shù)得到異步方法執(zhí)行的結(jié)果(Result屬性)和異步操作執(zhí)行過(guò)程中拋出的異常(Error屬性),以及得到在執(zhí)行異步操作顯式指定的信息(UserState)。AddCompleteEventArgs和AsyncCompleteEventArgs的定義如下。
1: public partial class AddCompleteEventArgs : AsyncCompleteEventArgs 2: { 3:? 4: public AddCompleteEventArgs(object[] results,Exception exception, bool cancelled, object userState) : 5: base(exception, cancelled, userState) 6: { 7: //省略實(shí)現(xiàn) 8: } 9:? 10: public double Result 11: { 12: get 13: { 14: //省略實(shí)現(xiàn) 15: } 16: } 17: }?
1: public class AsyncCompleteEventArgs : EventArgs 2: { 3: public bool Cancelled { get; } 4: public Exception Error { get; } 5: public object UserState { get; } 6: }二、通過(guò)BeginXxx/EndXxx進(jìn)行異步服務(wù)調(diào)用
接下來(lái)我將介紹3種不同的執(zhí)行異步服務(wù)調(diào)用的方式,為了簡(jiǎn)單起見(jiàn),我們以上面提到的CalculatorService為例演示通過(guò)異步操作得到運(yùn)算結(jié)果,并將結(jié)果輸出。首先采用傳統(tǒng)的異步編程模式BeginXxx/EndXxx,如下面的代碼所示,在調(diào)用BeginAdd方法后,可以做一些額外的處理工作,這些工作將會(huì)和Add服務(wù)操作的調(diào)用并發(fā)地運(yùn)行,最終的運(yùn)算結(jié)果通過(guò)EndAdd方法得到。
1: CalculateClient proxy = new CalculateClient(); 2: IAsyncResult asynResult = proxy.BeginAdd(1, 2, null, null); 3: //其他操作 4: double result = proxy.EndAdd(asynResult); 5: proxy.Close(); 6: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, result);三、通過(guò)回調(diào)的方式進(jìn)行異步服務(wù)調(diào)用
通過(guò)上面的方式進(jìn)行異步調(diào)用有一個(gè)不好的地方,就是當(dāng)EndAdd方法被執(zhí)行的時(shí)候,如果異步執(zhí)行的方法Add沒(méi)有執(zhí)行結(jié)束的話,該方法將會(huì)阻塞當(dāng)前線程并等待異步方法的結(jié)束,往往不能起到地多線程并發(fā)執(zhí)行應(yīng)有的作用。我們真正希望的是在異步執(zhí)行結(jié)束后自動(dòng)回調(diào)設(shè)定的操作,這樣就可以采用回調(diào)的方式來(lái)實(shí)現(xiàn)這樣的機(jī)制了。
在下面的代碼中,我們通過(guò)一個(gè)匿名方法的形式定義回調(diào)操作,由于在回調(diào)操用中輸出運(yùn)算結(jié)果時(shí)需要使用到參與運(yùn)算的操作數(shù),我們通過(guò)BeginAdd方法的最后一個(gè)object類(lèi)型參數(shù)實(shí)現(xiàn)向回調(diào)操作傳遞數(shù)據(jù),在回調(diào)操作中通過(guò)IAsyncResult對(duì)象的AsyncState獲得。
1: CalculateClient proxy = new CalculateClient(); 2: proxy.BeginAdd(1, 2, 3: delegate(IAsyncResult asyncResult) 4: { 5: double[] operands = asyncResult.AsyncState as double[]; 6: double result = proxy.EndAdd(asyncResult); 7: proxy.Close(); 8: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", operands[0], operands[1], result); 9: }, new double[]{1,2});四、通過(guò)事件注冊(cè)的方式進(jìn)行異步服務(wù)調(diào)用
實(shí)際上,事件注冊(cè)和通過(guò)回調(diào)從表現(xiàn)上看比較類(lèi)似,當(dāng)操作結(jié)束之后,對(duì)于前者通過(guò)觸發(fā)事件的方式執(zhí)行相應(yīng)的操作,而對(duì)于后者直接執(zhí)行指定的回調(diào)操作。如果采用事件注冊(cè)的方式,上面的代碼就可以改寫(xiě)成下面的形式。通過(guò)AddAsync開(kāi)始異步操作,如果需要向AddComplete事件傳遞數(shù)據(jù),可以使用該方法的第3個(gè)參數(shù)userState(該參數(shù)和BeginAdd的第4個(gè)參數(shù)asyncState具有相似的作用),設(shè)定的值可以通過(guò)AddCompleteEventArgs的UserState屬性獲得,而操作執(zhí)行的結(jié)果則通過(guò)AddCompleteEventArgs的Result屬性獲得。
1: CalculateClient proxy = new CalculateClient(); 2: proxy.AddComplete += delegate(object sender, AddCompleteEventArgs args) 3: { 4: double[] operands = args.UserState as double[]; 5: double result = args.Result; 6: proxy.Close(); 7: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", operands[0], operands[1], result); 8: }; 9: proxy.AddAsync(1, 2,new double[]{1,2}); 作者:Artech出處:http://artech.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
總結(jié)
以上是生活随笔為你收集整理的WCF技术剖析之十一:异步操作在WCF中的应用(上篇)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: lwbt的内存分配详解
- 下一篇: RHEL4- DNS服务(四)DNS的开