.Net組件程序設(shè)計(jì)之異步調(diào)用
成都創(chuàng)新互聯(lián)是一家網(wǎng)站設(shè)計(jì)公司,集創(chuàng)意、互聯(lián)網(wǎng)應(yīng)用、軟件技術(shù)為一體的創(chuàng)意網(wǎng)站建設(shè)服務(wù)商,主營產(chǎn)品:成都響應(yīng)式網(wǎng)站建設(shè)公司、品牌網(wǎng)站制作、成都全網(wǎng)營銷。我們專注企業(yè)品牌在網(wǎng)站中的整體樹立,網(wǎng)絡(luò)互動的體驗(yàn),以及在手機(jī)等移動端的優(yōu)質(zhì)呈現(xiàn)。成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、移動互聯(lián)產(chǎn)品、網(wǎng)絡(luò)運(yùn)營、VI設(shè)計(jì)、云產(chǎn)品.運(yùn)維為核心業(yè)務(wù)。為用戶提供一站式解決方案,我們深知市場的競爭激烈,認(rèn)真對待每位客戶,為客戶提供賞析悅目的作品,網(wǎng)站的價值服務(wù)。說到異步調(diào)用,在腦海中首先想到就是BeginInvoke(),在一些常用對象中我們也會常常見到Invoke()和BeginInvoke(), 要想讓自己的組件可以被客戶端調(diào)用或者是異步調(diào)用,這樣的設(shè)計(jì)是合理的,這也是組件異步機(jī)制當(dāng)中的一條 (說句題外話--其實(shí)大多數(shù)知識都隱藏在我們平時經(jīng)常見到的對象或者是代碼里,只不過是沒有去細(xì)心的發(fā)現(xiàn)) 在.NET中首先就會想到使用委托來進(jìn)行異步調(diào)用,關(guān)于委托的定義在 委托與事件一文中已經(jīng)大概的說過了,文中只是對委托進(jìn)行了 大概的講解,并沒有對委托的使用來說明或者是例舉一些示例。 在本篇中將會對委托進(jìn)行一個基礎(chǔ)的揭底,主要方向是異步調(diào)用。
一 委托的老調(diào)重彈
1 public class Operation 2 { 3 public int Addition(int num1, int num2) 4 { 5 return num1 + num2; 6 } 7 public int Subtraction(int num1, int num2) 8 { 9 return num1 - num2; 10 } 11 }
沒有必要直接使用Operation對象來進(jìn)行加減運(yùn)算,可以使用委托:
1 public delegate int OperationDelegate(int num1, int num2); 2 3 Operation operation = new Operation(); 4 OperationDelegate Additiondelegate = operation.Addition; 5 6 int result; 7 result = Additiondelegate.Invoke(3, 4); 8 Debug.Assert(result == 7);
在使用委托進(jìn)行調(diào)用的時候,當(dāng)前線程是被阻塞的,只有當(dāng)委托執(zhí)行完畢了,才會把控制權(quán)交回到當(dāng)前線程。
不過呢,委托可以用于進(jìn)行異步調(diào)用目標(biāo)方法的,委托只是一種特定的類型,編譯器會把我們定義的各式各樣的委托編譯成
對應(yīng)的類,好比OperationDelegate委托一樣,實(shí)則是被編譯成這樣的
1 public sealed class OperationDelegate : MulticastDelegate 2 { 3 public OperationDelegate(Object target, int methodPtr) { } 4 public virtual Invoke(int num1,int num2) 5 { 6 …… 7 } 8 9 public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback 10 11 callback,object asyncState) 12 { 13 …… 14 } 15 16 public virtual int EndInvoke(IAsyncResult result) 17 { 18 …… 19 } 20 }
這里只是回顧一下委托的定義。
二 異步調(diào)用編程模型
圖1
在上圖我們所見的有這幾個模塊, .NET線程池、異步調(diào)用請求隊(duì)列和一個應(yīng)用程序的主線程。
假使現(xiàn)在從任務(wù)1開始執(zhí)行到任務(wù)2、任務(wù)3,到了任務(wù)3的時候,任務(wù)3請求.NET執(zhí)行異步操作,如圖2
圖2
這個時候【任務(wù)3】已經(jīng)被送入到了【異步請求隊(duì)列】中,并且主線程是阻塞狀態(tài)的,再看圖3的執(zhí)行過程:
圖3
線程池會及時的發(fā)現(xiàn)【異步請求隊(duì)列】中的任務(wù),并且根據(jù)任務(wù)的信息,線程池會分配一個線程到任務(wù)所在的主線程中執(zhí)行所請求的任務(wù)。 在異步任務(wù)執(zhí)行時,這個時候主線程才會從阻塞中撤銷,進(jìn)入執(zhí)行狀態(tài),上圖中,就是開始執(zhí)行任務(wù)4。
這里要說的就是,異步調(diào)用看起來是并行執(zhí)行的,實(shí)際剛開始的時候還是順序的,不過這時間在實(shí)際情況中是忽略不計(jì)的, 可以認(rèn)為就是并行執(zhí)行的吧。
三 BeginInvoke()、EndInvoke()
3.1 BeginInvoke()
BeginInvoke()函數(shù)定義如下:
1 public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback callback,object asyncState) 2 { 3 …… 4 }
接受OperationDelegate委托定義的原始簽名的輸入?yún)?shù),還有兩個額外參數(shù),AsyncCallback是系統(tǒng)定義的委托, 用于異步調(diào)用完成時回調(diào)所用,這里不做講解,后面會有講到,還有一個是參數(shù)是一個狀態(tài)對象,也可以認(rèn)為是容器對象, 也會在后面的章節(jié)中講到。
1 Operation operation = new Operation(); 2 OperationDelegate Additiondelegate = operation.Addition; 3 Additiondelegate.BeginInvoke(3, 4, null, null);
3.2 IAsyncResult接口
正如上面所看到的,BeginInvoke函數(shù)返回一個IAsyncResult類型的值,那就來看一下IAsyncResult的定義:
1 public interface IAsyncResult 2 { 3 object AsyncState { get; } 4 WaitHandle AsyncWaitHandle { get; } 5 bool CompletedSynchronously { get; } 6 bool IsCompleted { get; } 7 }
對于IAsyncResult的詳細(xì)用法 稍后會有講解
看到第一節(jié)的Invoke函數(shù)執(zhí)行后,可以直接獲取到返回值,怎么這個BeginInvoke函數(shù)執(zhí)行了返回
IAsyncResult類型,返回值在哪呢? 可以通過從BeginInvoke函數(shù)獲得的IAsyncResult交給EndInvoke函數(shù)來獲取返回值。
1 Operation operation = new Operation(); 2 OperationDelegate Additiondelegate = operation.Addition; 3 4 IAsyncResult asyncResult = Additiondelegate.BeginInvoke(3, 4, null, null); 5 int result = Additiondelegate.EndInvoke(asyncResult); 6 Debug.Assert(result == 7);
這里要說幾點(diǎn)
第一.調(diào)用EndInvoke函數(shù)的時候,當(dāng)前線程是被阻塞的,它在等待BeginInvoke函數(shù)執(zhí)行完畢。
第二.雖然委托可以管理多個目標(biāo)方法,但是在異步調(diào)用中,所執(zhí)行異步調(diào)用的委托,內(nèi)部的管理列表只能有一個目標(biāo)方法,不然會報 有異常。
第三.EndInvoke()在每次異步調(diào)用操作時 只能調(diào)用一次。
第四.BeginInvoke()返回的IAsyncResult類型的實(shí)例,只能傳入它所調(diào)用BeginInvoke()委托的EndInvoke()中,不然也會報有異常。
3.3 AsyncResult
假使一個客戶端在一個代碼段或者是函數(shù)中使用BeginInvoke(),而在另一段或者是其他的函數(shù)中調(diào)用EndInvoke(),這樣客戶端是不是就要保存IAsyncResult對象,又或者一個客戶端發(fā)起異步調(diào)用,并且由另一個 客戶端來調(diào)用EndInvoke(),這不僅僅要保存IAsyncResult對象,還需要保存該委托對象,而且你還得傳送過去。 還好.NET是那么的機(jī)智,有System.Runtime.Remoting.Messaging.AsyncResult類型的存在。
public class AsyncResult : IAsyncResult, IMessageSink { #region IAsyncResult 成員 public object AsyncState { get { throw new NotImplementedException(); } } public System.Threading.WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { throw new NotImplementedException(); } } public bool IsCompleted { get { throw new NotImplementedException(); } } #endregion public bool EndInvokeCalled { get; set; } public virtual object AsyncDelegate { get; } //IMessageSink 成員 }
看著上面有個AsyncDelegate的屬性,會不會覺得很漂亮,不錯,它就是原始發(fā)起委托的引用,看下如何使用AsyncDelegate來使用EndInvoke():
1 public class OperationTest 2 { 3 4 public void Test() 5 { 6 Operation operation = new Operation(); 7 OperationDelegate Additiondelegate = operation.Addition; 8 int Result; 9 Result = GetResult(Additiondelegate.BeginInvoke(3, 4, null, null)); 10 } 11 12 private int GetResult(IAsyncResult asyncresult) 13 { 14 AsyncResult asyncResult = (AsyncResult)asyncresult; 15 OperationDelegate operationdelegate = asyncResult.AsyncDelegate as 16 17 OperationDelegate; 18 if (operationdelegate != null) 19 { 20 Debug.Assert(asyncResult.EndInvokeCalled == false);//EndInvoke()是否被調(diào)用過 21 return operationdelegate.EndInvoke(asyncResult); 22 } 23 return -1; 24 } 25 }
3.4 輪循或等待
看到這里,善于思考的朋友會發(fā)現(xiàn),還存在著一個很大的問題,就是發(fā)起異步調(diào)用的客戶端,如何知道自己 的異步函數(shù)是否執(zhí)行完畢了?或者是想等待一會,做一些其他的處理,然后再繼續(xù)等待,該怎么來實(shí)現(xiàn)呢?
從BeginInvoke()返回的IAsyncResult接口有個AsyncWaitHandle屬性,它是干嗎的呢?就把它理解為消息接收器吧。
1 Operation operation = new Operation(); 2 OperationDelegate Additiondelegate = operation.Addition; 3 IAsyncResult asyncResult = Additiondelegate.BeginInvoke(2, 3, null, null); 4 asyncResult.AsyncWaitHandle.WaitOne();//如果任務(wù)完成則不會阻塞 否則阻塞當(dāng)前線程 5 int Result; 6 Result = Additiondelegate.EndInvoke(asyncResult); 7 Debug.Assert(Result == 5);
代碼和3.2的幾乎相同,區(qū)別就是這段代碼保證了EndInvoke()的調(diào)用者不會被阻塞。
看一下等待一下,如果沒完成處理其他任務(wù),回來再等待是怎么實(shí)現(xiàn)的。
1 Operation operation = new Operation(); 2 OperationDelegate Additiondelegate = operation.Addition; 3 IAsyncResult asyncResult = Additiondelegate.BeginInvoke(2, 3, null, null); 4 while (asyncResult.IsCompleted == false)//判斷異步任務(wù)是否完成 5 { 6 asyncResult.AsyncWaitHandle.WaitOne(10,false);//如果任務(wù)完成則不會阻塞 否則阻塞當(dāng)前線程10毫秒 7 //這里做一些其他操作 8 } 9 int Result; 10 Result = Additiondelegate.EndInvoke(asyncResult); 11 Debug.Assert(Result == 5);
3.5 使用回調(diào)函數(shù)
現(xiàn)在我們要來說說BeginInvoke()的第三個參數(shù)了, public delegate void AsyncCallback(IAsyncResult ar);
第三個參數(shù)就是系統(tǒng)提供的一個委托類型,委托簽名也都看到了。 使用回調(diào)函數(shù)的好處就是不需要去處理等待操作了,因?yàn)樵诋惒饺蝿?wù)完成的時候, 會調(diào)用你傳給BeginInvoke()里AsyncCallback委托所關(guān)聯(lián)的目標(biāo)方法。
1 public class OperationTest 2 { 3 4 public void Test() 5 { 6 Operation operation = new Operation(); 7 OperationDelegate Additiondelegate = operation.Addition; 8 9 Additiondelegate.BeginInvoke(2, 3, new AsyncCallback(OnCallBack), null); 10 } 11 12 private void OnCallBack(IAsyncResult asyncresult) 13 { 14 AsyncResult asyncResult = (AsyncResult)asyncresult; 15 OperationDelegate operationdelegate = asyncResult.AsyncDelegate as 16 17 OperationDelegate; 18 if (operationdelegate != null) 19 { 20 Debug.Assert(asyncResult.EndInvokeCalled == false); 21 int result=operationdelegate.EndInvoke(asyncResult); 22 Console.WriteLine("Operation returned" + result.ToString()); 23 } 24 } 25 }
這里需要說的是在異步任務(wù)完成時,執(zhí)行的回調(diào)函數(shù)依然是在子線程當(dāng)中,并不是在主線程中執(zhí)行回調(diào)函數(shù)的。
題外話:最常見的就是在Winform開發(fā)中,F(xiàn)orm中發(fā)起異步調(diào)用,然后回調(diào)函數(shù)操作Form中的控件或者是
值的時候就會報錯, 就是這個原因,因?yàn)樗鼈儾辉谝粋€線程也不在一個上下文中,基于.NET安全策略這種操作是不允許的。
END
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。