概述html
在本篇隨筆中,經過一些簡單的示例來講一下Web Service中的異步調用模式。調用Web Service方法有兩種方式,同步調用和異步調用。同步調用是程序繼續執行前等候調用的完成,而異步調用在後臺繼續時,程序也繼續執行,沒必要等待方法處理完成而直接返回。具體的調用流程見下圖:編程
對於同步調用方法而言,UI線程依賴於方法的實現,方法執行時間過長將致使UI沒法及時與用戶進行交互。咱們知道,在Windows客戶端中,每一個進程都有單一的UI進程,在服務器中,可擴展性依賴於線程的使用。對於異步調用方法而言,可以及時於用戶交互響應,從而提供了良好的用戶體驗;同時也能夠改善服務器的可擴展性,將服務器與通信問題隔離。設計模式
客戶端異步調用方法服務器
在客戶端異步調用是徹底基於Proxy的方法,異步行爲最簡單的模式。Visual Studio和WSDL.EXE提供對它的直接支持。因此咱們沒必要在Web服務應用程序中編寫額外的代碼來處理異步調用。併發
遍佈.NET Framework的異步調用有一個基礎的設計模式:Begin方法和End方法,他們分別用於開始和終止異步處理。Visual Studio和WSDL.exe生成了這兩種方法:異步
Begin<WebServiceMethodName>——該方法通知Web服務開始處理調用,並當即返回。該方法不返回Web服務調用所指定的數據類型,而是返回一種實現IasyncResult接口的數據類型。async
End<WebServiceMethodName>——該方法通知Web服務返回先前啓動的Web方法所生成的結果。ide
IasyncResult接口包含了WaitHandle類型的AsyncWaitHandle特性。這個公共接口容許用戶的客戶應用程序等待調用,並且,該接口將用Any或All語義(例如WaitHandle.WaitOne,WaitAny和WaitAll)做爲信號通知客戶應用程序。例如,若是想要客戶應用程序異步等候一個Web方法,可調用WaitOne來處理要完成的Web服務。函數
通常來講,客戶端異步代理方法有兩種實現機制:使用同步對象和回調機制(也許你可能對用這個詞不習慣,實在找不到第二個詞來代替,暫且這樣稱呼吧)this
同步對象
同步對象容許用戶對Web服務的方法進行調用(使用Begin方法),而後繼續處理。在後面的程序中,能夠調用End方法,傳遞同步對象,以便獲得調用結果。這種方式下,可以繼續執行函數中的程序流程,而不執行回調處理。在這裏調用WaitOne()方法會掛起當前線程,避免忙等待的發生,直到Web Services方法調用結束返回後,該線程纔會被從新喚起。
示例代碼:
/// <summary> /// 利用同步對象實現異步調用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_AsyncClient_Click(object sender, System.EventArgs e) { IAsyncResult ar = wsc.BeginHello(this.txt_UserName.Text, null, null); MessageBox.Show("Continue to do some other things"); ar.AsyncWaitHandle.WaitOne(); strHello = wsc.EndHello(ar); this.rtb_Result.Text = strHello; }
回調機制
從本質上說,異步回調機制是委託的.NET等價物,它經過在異步操做完成時創建一個被調用的單獨方法來進行工做。調用應用程序可以繼續處理其餘的任務,直到回調函數被調用爲止。這就意味着處理已經完成了,應用程序能夠正常運行了。使用同步對象不一樣於回調機制的區別是,當檢查Web方法是否已經完成,以及檢查Web方法中是否含有須要的結果時,咱們沒法對其進行控制,而在回調的狀況中,Web方法一旦完成,這些工做就會被自動執行。
示例代碼:
/// <summary> /// 顯示結果 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void UpdateResult(object sender, EventArgs e) { this.rtb_Result.Text = strHello; } public void OnHelloComplete(IAsyncResult ar) { strHello = wsc.EndHello(ar); this.rtb_Result.Invoke(new EventHandler(UpdateResult)); } /// <summary> /// 用回調機制實現異步調用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_CallBack_Click(object sender, System.EventArgs e) { AsyncCallback cb = new AsyncCallback(OnHelloComplete); wsc.BeginHello(this.txt_UserName.Text, cb, null); }
使用回調機制仍是同步對象取決於用戶所面臨的具體狀況。在檢查異步調用是否完成時,若是願意對處理過程進行控制,那麼能夠選擇使用同步對象。若是以爲本身編寫代碼來完成對Web服務的調用,且當方法一旦執行完畢就當即由所調用的特殊函數來處理所返回的結果更適合一些,那麼就更適合用回調機制。
在客戶端使用異步方法調用,能夠改進UI響應度,在服務器端不須要實現異步操做,對服務器來講是透明的,並且客戶端可以在任什麼時候間選擇阻塞。
服務端使用Soap One-Way方法
在服務器端使用One-Way方法實現異步調用,其實質是將單項消息發送到端點。這種方式的特色是方法沒有返回值,客戶端方法不會從調用的服務器端方法中收到返回值;咱們沒法判斷方法結束的時間,對於結果須要顯式通知或者輪詢。
在Web 服務端,咱們使用[SoapDocumentMethod]定義One-Way方法:
示例代碼:
/// <summary> /// One-Way方式的異步調用Set /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_OneWay_Click(object sender, System.EventArgs e) { wsc.SetHello(this.txt_UserName.Text); } /// <summary> /// One-Way方式的異步調用Get /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_onewayGet_Click(object sender, System.EventArgs e) { strHello = wsc.GetHello(); this.rtb_Result.Text = strHello; }
One-Way方法不適合於下列狀況:
l 方法須要對結果輪詢
l 方法須要同步
服務端使用WSE SoapSender和SoapRecevier
在進行本部份內容以前,咱們須要安裝WSE2.0。WSE支持面向消息的編程,爲咱們提供了SoapSender和SoapReceiver基類,它可以支持發送和接收SoapEnvelopes,同時它也經過SoapClient和SoapService提供了更多的事務支持。SoapSender和SoapReceiver在客戶端和服務端同時實現,客戶端使用SoapSender發送消息,同時可選擇使用SoapReceiver接收消息;服務端使用SoapReceiver接收消息,同時也能夠選擇使用SoapSender發送通知和迴應。
示例代碼:
客戶端:
/// <summary> /// 自定義的消息接收類 /// </summary> public class MyReceiver: SoapReceiver { public static Form1 form; private string strBody; protected override void Receive(SoapEnvelope envelope) { strBody = envelope.InnerText; ///注意:在進行此項以前,必定要把rtb_Result控件的屬性設爲Public form.rtb_Result.Invoke(new EventHandler(UpdateBody)); } void UpdateBody(object sender, System.EventArgs e) { form.rtb_Result.Text = strBody; } } /**************************************************/ /// <summary> /// 用WSE實現異步調用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button4_Click(object sender, System.EventArgs e) { wsc.FireEvent(); }
Web Service端:
private ArrayList Listeners { get { return (ArrayList)Application["Listeners"]; } } [WebMethod] public void AddListener(string listener) { ArrayList alist = (ArrayList)Application["Listeners"]; if(alist == null) alist = new ArrayList(); alist.Add(listener); Application["Listeners"] = alist; } [WebMethod] public void FireEvent() { int i; for(i = 0;i < this.Listeners.Count;i++) { SoapEnvelope envelope = new SoapEnvelope(); envelope.SetBodyObject("Hello World!"); envelope.Context.Addressing.Action = new Action((string)(this.Listeners[i])); envelope.Context.Addressing.ReplyTo = new ReplyTo(new System.Uri((string)(this.Listeners[i]))); SoapSender peerProxy = new SoapSender(new System.Uri((string)(this.Listeners[i]))); peerProxy.Send(envelope); } }
服務端使用WSE 自定義SoapMSMQ傳輸
SoapMSMQ是一款開源軟件,簡化使用WSE進行MSMQ操做,下載地址:
http://www.codeproject.com/useritems/SoapMSMQ.asp
SoapMSMQ徹底支持事務,具備以下特色:
l 在事務中,請求要被同步初始化
l 同步階段排隊請求,而且返回令牌
l 異步階段處理各個事務
l 全部持有令牌的請求都保證會被處理,但可能會不成功
l 支持向客戶端發送通知
對SoapMSMQ感興趣的朋友能夠下載下來後,作進一步的研究。
總結
異步方法調用改善了客戶端的響應和用戶體驗,增長了服務端的可擴展性。當方法須要耗費大量的時間時,能夠採用異步方式調用,提供系統併發處理的能力。對於異步方式的開發,咱們能夠有如上所述的普遍選擇。
示例程序界面:
下載地址:http://files.cnblogs.com/Terrylee/AsyncDemo.rar
出處:http://www.cnblogs.com/Terrylee/archive/2005/12/05/290845.html