深入學(xué)習(xí)Web Service系列之
異步開(kāi)發(fā)模式
——《深入學(xué)習(xí)
Web Service
系列》之一
Terrylee
,
2005
年
12
月
4
日
概述
在本篇隨筆中,通過(guò)一些簡(jiǎn)單的示例來(lái)說(shuō)一下
Web Service
中的異步調(diào)用模式。調(diào)用
Web Service
方法有兩種方式,同步調(diào)用和異步調(diào)用。同步調(diào)用是程序繼續(xù)執(zhí)行前等候調(diào)用的完成,而異步調(diào)用在后臺(tái)繼續(xù)時(shí),程序也繼續(xù)執(zhí)行,不必等待方法處理完成而直接返回。具體的調(diào)用流程見(jiàn)下圖:
?
對(duì)于同步調(diào)用方法而言,
UI
線程依賴于方法的實(shí)現(xiàn),方法執(zhí)行時(shí)間過(guò)長(zhǎng)將導(dǎo)致
UI
無(wú)法及時(shí)與用戶進(jìn)行交互。我們知道,在
Windows
客戶端中,每個(gè)進(jìn)程都有單一的
UI
進(jìn)程,在服務(wù)器中,可擴(kuò)展性依賴于線程的使用。對(duì)于異步調(diào)用方法而言,能夠及時(shí)于用戶交互響應(yīng),從而提供了良好的用戶體驗(yàn);同時(shí)也可以改善服務(wù)器的可擴(kuò)展性,將服務(wù)器與通訊問(wèn)題隔離。
客戶端異步調(diào)用方法
在客戶端異步調(diào)用是完全基于
Proxy
的方法,異步行為最簡(jiǎn)單的模式。
Visual Studio
和
WSDL.EXE
提供對(duì)它的直接支持。所以我們不必在
Web
服務(wù)應(yīng)用程序中編寫(xiě)額外的代碼來(lái)處理異步調(diào)用。
遍及
.NET Framework
的異步調(diào)用有一個(gè)基礎(chǔ)的設(shè)計(jì)模式:
Begin
方法和
End
方法,他們分別用于開(kāi)始和終止異步處理。
Visual Studio
和
WSDL.exe
生成了這兩種方法:
Begin<WebServiceMethodName>
——該方法通知
Web
服務(wù)開(kāi)始處理調(diào)用,并立即返回。該方法不返回
Web
服務(wù)調(diào)用所指定的數(shù)據(jù)類型,而是返回一種實(shí)現(xiàn)
IasyncResult
接口的數(shù)據(jù)類型。
End<WebServiceMethodName>
——該方法通知
Web
服務(wù)返回先前啟動(dòng)的
Web
方法所生成的結(jié)果。
IasyncResult
接口包含了
WaitHandle
類型的
AsyncWaitHandle
特性。這個(gè)公共接口允許用戶的客戶應(yīng)用程序等待調(diào)用,而且,該接口將用
Any
或
All
語(yǔ)義(例如
WaitHandle.WaitOne
,
WaitAny
和
WaitAll
)作為信號(hào)通知客戶應(yīng)用程序。例如,如果想要客戶應(yīng)用程序異步等候一個(gè)
Web
方法,可調(diào)用
WaitOne
來(lái)處理要完成的
Web
服務(wù)。
一般來(lái)說(shuō),客戶端異步代理方法有兩種實(shí)現(xiàn)機(jī)制:使用同步對(duì)象和回調(diào)機(jī)制(也許你可能對(duì)用這個(gè)詞不習(xí)慣,實(shí)在找不到第二個(gè)詞來(lái)代替,暫且這樣稱呼吧)
同步對(duì)象
同步對(duì)象允許用戶對(duì)
Web
服務(wù)的方法進(jìn)行調(diào)用(使用
Begin
方法),然后繼續(xù)處理。在后面的程序中,可以調(diào)用
End
方法,傳遞同步對(duì)象,以便得到調(diào)用結(jié)果。這種方式下,能夠繼續(xù)執(zhí)行函數(shù)中的程序流程,而不執(zhí)行回調(diào)處理。在這里
調(diào)用
WaitOne()
方法會(huì)掛起當(dāng)前線程,避免忙等待的發(fā)生,直到
Web Services
方法調(diào)用結(jié)束返回后,該線程才會(huì)被重新喚起。
示例代碼:
?1
/**/
///
?
<summary>
?2
///
?利用同步對(duì)象實(shí)現(xiàn)異步調(diào)用
?3
///
?
</summary>
?4
///
?
<param?name="sender"></param>
?5
///
?
<param?name="e"></param>
?6
private
?
void
?btn_AsyncClient_Click(
object
?sender,?System.EventArgs?e)
?7
{
?8
????IAsyncResult?ar?
=
?wsc.BeginHello(
this
.txt_UserName.Text,?
null
,?
null
);
?9
10
11
????MessageBox.Show(
"
Continue?to?do?some?other?things
"
);
12
13
????ar.AsyncWaitHandle.WaitOne();
14
15
????strHello?
=
?wsc.EndHello(ar);
16
17
????
this
.rtb_Result.Text?
=
?strHello;
18
}
回調(diào)機(jī)制
從本質(zhì)上說(shuō),異步回調(diào)機(jī)制是委托的
.NET
等價(jià)物,它通過(guò)在異步操作完成時(shí)建立一個(gè)被調(diào)用的單獨(dú)方法來(lái)進(jìn)行工作。調(diào)用應(yīng)用程序能夠繼續(xù)處理其他的任務(wù),直到回調(diào)函數(shù)被調(diào)用為止。這就意味著處理已經(jīng)完成了,應(yīng)用程序可以正常運(yùn)行了。使用同步對(duì)象不同于回調(diào)機(jī)制的區(qū)別是,當(dāng)檢查
Web
方法是否已經(jīng)完成,以及檢查
Web
方法中是否含有需要的結(jié)果時(shí),我們無(wú)法對(duì)其進(jìn)行控制,而在回調(diào)的情況中,
Web
方法一旦完成,這些工作就會(huì)被自動(dòng)執(zhí)行。
示例代碼:
?1
/**/
///
?
<summary>
?2
///
?顯示結(jié)果
?3
///
?
</summary>
?4
///
?
<param?name="sender"></param>
?5
///
?
<param?name="e"></param>
?6
public
?
void
?UpdateResult(
object
?sender,?EventArgs?e)
?7
{
?8
????
this
.rtb_Result.Text?
=
?strHello;
?9
}
10
11
public
?
void
?OnHelloComplete(IAsyncResult?ar)
12
{
13
????strHello?
=
?wsc.EndHello(ar);
14
15
????
this
.rtb_Result.Invoke(
new
?EventHandler(UpdateResult));
16
}
17
18
/**/
///
?
<summary>
19
///
?用回調(diào)機(jī)制實(shí)現(xiàn)異步調(diào)用
20
///
?
</summary>
21
///
?
<param?name="sender"></param>
22
///
?
<param?name="e"></param>
23
private
?
void
?btn_CallBack_Click(
object
?sender,?System.EventArgs?e)
24
{
25
????AsyncCallback?cb?
=
?
new
?AsyncCallback(OnHelloComplete);
26
27
????wsc.BeginHello(
this
.txt_UserName.Text,?cb,?
null
);
28
}
使用回調(diào)機(jī)制還是同步對(duì)象取決于用戶所面臨的具體情況。在檢查異步調(diào)用是否完成時(shí),如果愿意對(duì)處理過(guò)程進(jìn)行控制,那么可以選擇使用同步對(duì)象。如果覺(jué)得自己編寫(xiě)代碼來(lái)完成對(duì)
Web
服務(wù)的調(diào)用,且當(dāng)方法一旦執(zhí)行完畢就立即由所調(diào)用的特殊函數(shù)來(lái)處理所返回的結(jié)果更適合一些,那么就更適合用回調(diào)機(jī)制。
在客戶端使用異步方法調(diào)用,可以改進(jìn)
UI
響應(yīng)度,在服務(wù)器端不需要實(shí)現(xiàn)異步操作,對(duì)服務(wù)器來(lái)說(shuō)是透明的,而且客戶端能夠在任何時(shí)間選擇阻塞。
服務(wù)端使用
Soap One-Way
方法
在服務(wù)器端使用
One-Way
方法實(shí)現(xiàn)異步調(diào)用,其實(shí)質(zhì)是將單項(xiàng)消息發(fā)送到端點(diǎn)。這種方式的特點(diǎn)是方法沒(méi)有返回值,客戶端方法不會(huì)從調(diào)用的服務(wù)器端方法中收到返回值;我們無(wú)法判斷方法結(jié)束的時(shí)間,對(duì)于結(jié)果需要顯式通知或者輪詢。
在
Web
服務(wù)端,我們使用
[SoapDocumentMethod]
定義
One-Way
方法:
?[System.Web.Services.Protocols.SoapDocumentMethod(OneWay
=
true
)]
示例代碼:
?1
/**/
///
?
<summary>
?2
///
?One-Way方式的異步調(diào)用Set
?3
///
?
</summary>
?4
///
?
<param?name="sender"></param>
?5
///
?
<param?name="e"></param>
?6
private
?
void
?btn_OneWay_Click(
object
?sender,?System.EventArgs?e)
?7
{
?8
????wsc.SetHello(
this
.txt_UserName.Text);????
?9
}
10
11
/**/
///
?
<summary>
12
///
?One-Way方式的異步調(diào)用Get
13
///
?
</summary>
14
///
?
<param?name="sender"></param>
15
///
?
<param?name="e"></param>
16
private
?
void
?btn_onewayGet_Click(
object
?sender,?System.EventArgs?e)
17
{
18
????strHello?
=
?wsc.GetHello();
19
20
????
this
.rtb_Result.Text?
=
?strHello;
21
}
One-Way
方法不適合于下列情況:
l????????
方法需要對(duì)結(jié)果輪詢
l????????
方法需要同步
服務(wù)端使用
WSE SoapSender
和
SoapRecevier
在進(jìn)行本部分內(nèi)容之前,我們需要安裝
WSE2.0
。
WSE
支持面向消息的編程,為我們提供了
SoapSender
和
SoapReceiver
基類,它能夠支持發(fā)送和接收
SoapEnvelopes
,同時(shí)它也通過(guò)
SoapClient
和
SoapService
提供了更多的事務(wù)支持。
SoapSender
和
SoapReceiver
在客戶端和服務(wù)端同時(shí)實(shí)現(xiàn),客戶端使用
SoapSender
發(fā)送消息,同時(shí)可選擇使用
SoapReceiver
接收消息;服務(wù)端使用
SoapReceiver
接收消息,同時(shí)也可以選擇使用
SoapSender
發(fā)送通知和回應(yīng)。
示例代碼:
?客戶端:
?1
/**/
///
?
<summary>
?2
????
///
?自定義的消息接收類
?3
????
///
?
</summary>
?4
????
public
?
class
?MyReceiver:?SoapReceiver
?5
????
{
?6
????????
public
?
static
?Form1?form;
?7
????????
private
?
string
?strBody;
?8
?9
????????
protected
?
override
?
void
?Receive(SoapEnvelope?envelope)
10
????????
{
11
????????????strBody?
=
?envelope.InnerText;
12
13
????????????
/**/
///
注意:在進(jìn)行此項(xiàng)之前,一定要把rtb_Result控件的屬性設(shè)為Public
14
????????????form.rtb_Result.Invoke(
new
?EventHandler(UpdateBody));
15
????????}
16
17
????????
void
?UpdateBody(
object
?sender,?System.EventArgs?e)
18
????????
{
19
????????????form.rtb_Result.Text?
=
?strBody;
20
????????}
21
????}
1
/**/
///
?
<summary>
2
///
?用WSE實(shí)現(xiàn)異步調(diào)用
3
///
?
</summary>
4
///
?
<param?name="sender"></param>
5
///
?
<param?name="e"></param>
6
private
?
void
?button4_Click(
object
?sender,?System.EventArgs?e)
7
{
8
????wsc.FireEvent();
9
}
Web Service端:
?1
private
?ArrayList?Listeners
?2
????????
{
?3
????????????
get
?4
????????????
{
?5
????????????????
return
?(ArrayList)Application[
"
Listeners
"
];
?6
????????????}
?7
????????}
?8
?9
????????[WebMethod]
10
????????
public
?
void
?AddListener(
string
?listener)
11
????????
{
12
????????????ArrayList?alist?
=
?(ArrayList)Application[
"
Listeners
"
];
13
14
????????????
if
(alist?
==
?
null
)
15
????????????????alist?
=
?
new
?ArrayList();
16
17
????????????alist.Add(listener);
18
19
????????????Application[
"
Listeners
"
]?
=
?alist;
20
21
????????}
22
23
????????[WebMethod]
24
????????
public
?
void
?FireEvent()
25
????????
{
26
????????????
int
?i;
27
28
????????????
for
(i?
=
?
0
;i?
<
?
this
.Listeners.Count;i
++
)
29
????????????
{
30
????????????????SoapEnvelope?envelope?
=
?
new
?SoapEnvelope();
31
32
????????????????envelope.SetBodyObject(
"
Hello?World!
"
);
33
34
????????????????envelope.Context.Addressing.Action?
=
?
new
?Action((
string
)(
this
.Listeners[i]));
35
36
????????????????envelope.Context.Addressing.ReplyTo?
=
?
new
?ReplyTo(
new
?System.Uri((
string
)(
this
.Listeners[i])));
37
38
????????????????SoapSender?peerProxy?
=
?
new
?SoapSender(
new
?System.Uri((
string
)(
this
.Listeners[i])));
39
40
????????????????peerProxy.Send(envelope);
41
????????????}
42
????????}
服務(wù)端使用
WSE
自定義
SoapMSMQ
傳輸
SoapMSMQ
是一款開(kāi)源軟件,簡(jiǎn)化使用
WSE
進(jìn)行
MSMQ
操作,下載地址:
http://www.codeproject.com/useritems/SoapMSMQ.asp
SoapMSMQ
完全支持事務(wù),具有如下特點(diǎn):
l????????
在事務(wù)中,請(qǐng)求要被同步初始化
l????????
同步階段排隊(duì)請(qǐng)求,并且返回令牌
l????????
異步階段處理各個(gè)事務(wù)
l????????
所有持有令牌的請(qǐng)求都保證會(huì)被處理,但可能會(huì)不成功
l????????
支持向客戶端發(fā)送通知
對(duì)
SoapMSMQ
感興趣的朋友可以下載下來(lái)后,做進(jìn)一步的研究。
總結(jié)
異步方法調(diào)用改善了客戶端的響應(yīng)和用戶體驗(yàn),增加了服務(wù)端的可擴(kuò)展性。當(dāng)方法需要耗費(fèi)大量的時(shí)間時(shí),可以采用異步方式調(diào)用,提供系統(tǒng)并發(fā)處理的能力。對(duì)于異步方式的開(kāi)發(fā),我們可以有如上所述的廣泛選擇。
示例程序界面:

下載地址:
http://www.cnblogs.com/Files/Terrylee/AsyncDemo.rar
原文地址:http://terrylee.cnblogs.com/archive/2005/12/05/290845.html