c#中的BeginInvoke和EndEndInvoke 摘要
摘要
異步這東西,真正用起來的時候,發現事情還是挺多的,最近在項目中用到了異步的知識,發現對它還是不了解,處理起來,走了不少彎路。覺得還是補一補還是很有必要的。
MSDN原文地址:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx
正文
.Net framework可以讓你異步調用任何方法。為達到這樣的目的,你可以定義一個與你要調用的方法的簽名相同的委托。公共語言運行時將自動為該委托定義與簽名相同的BeginInvok和EndInvoke方法。
異步的委托調用方法BeginInvok和EndInvoke,在.NET Compact Framework中并沒有得到支持。
BeginInvoke方法觸發你的異步方法,它和你想要執行的異步方法有相同的參數。另外還有兩個可選參數,第一個是AsyncCallback委托是異步完成的回調方法。第二個是用戶自定義對象,該對象將傳遞到回調方法中。BeginInvoke立即返回并且不等待完成異步的調用(繼續執行該方法調用下面的代碼,不需要等待)。BeginInvoke返回IAsyncResult接口,可用于檢測異步調用方法線程的狀態。
通過EndInvoke方法檢測異步調用的結果。如果異步調用尚未完成,EndInvoke將阻塞調用線程,直到它完成。EndInvoke參數包括out和ref參數。
下面代碼演示使用BeginInvoke和EndInvoke進行異步調用的四種常見方式。在調用BeginInvoke可以做以下工作:
- 做一些其他操作,然后調用EndInvoke方法阻塞線程直到該方法完成。
- 使用IAsyncResult.AsyncWaitHandle屬性,使用它的WaitOne方法阻塞線程直到收到WaitHandle信號,然后調用EndInvoke。
- 檢查BeginInvoke返回值IAsyncResult的狀態來決定方法是否完成,然后調用EndInvoke方法。
- 通過在BeginInvoke方法中傳遞該委托,在回調方法中調用該委托的EndInvoke方法。
注意
無論你怎么使用,都必須調用EndInvoke方法結束你的異步調用。
下面通過模擬一個耗時的操作,實現上面說的那四種情況。
- 情況一:通過EndInovke阻塞線程,直到異步調用結束。
結果
由上圖,可以看出,在BeginInvoke開啟異步執行方法,會先執行其他的業務。通過EndInvoke方法,阻塞直到異步執行完畢,才會執行EndInvoke之后的代碼。
- 情況二:通過WaitHandle屬性阻塞線程。
你可以獲得BeginInvoke的返回值的WaitHandle,并使用它的AsyncWaitHanlde屬性。WaitHandle信號異步完成時,你可以通過調用WaitOne方法等待。
如果你使用WaitHandle,你可以在之前或者異步調用完成后進行其他的操作。但之后必須使用EndInvoke檢查結果。
注意
當你調用EndInvoke方法時,等待句柄并不會自動關閉。如果你釋放等待處理的所有引用,當垃圾回收等待句柄是,系統資源將被釋放。一旦你完成使用等待句柄,通過WaitHandle的close方法,一次性顯示關閉,這時的垃圾回收效率更高。~
using System; using System.Diagnostics; using System.Threading;namespace BeginInvokeDemo {/// <summary>/// 委托必須和要調用的異步方法有相同的簽名/// </summary>/// <param name="callDuration">sleep時間</param>/// <param name="threadId">當前線程id</param>/// <returns></returns>public delegate string AsyncMethodCaller(int callDuration, out int threadId);class Program{/// <summary>/// 主函數/// </summary>/// <param name="args"></param>static void Main(string[] args){AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);int threadid = 0;//開啟異步操作IAsyncResult result = caller.BeginInvoke(3000, out threadid, null, null);for (int i = 0; i < 10; i++){Console.WriteLine("其它業務" + i.ToString());}//調用EndInvoke,等待異步執行完成Console.WriteLine("等待異步方法TestMethodAsync執行完成");//等待異步執行完畢信號result.AsyncWaitHandle.WaitOne();Console.WriteLine("收到WaitHandle信號");//通過EndInvoke檢查結果string res = caller.EndInvoke(out threadid, result);//顯示關閉句柄result.AsyncWaitHandle.Close();Console.WriteLine("關閉了WaitHandle句柄");Console.WriteLine("Completed!");Console.WriteLine(res);Console.Read();}/// <summary>/// 與委托對應的方法/// </summary>/// <param name="callDuration"></param>/// <param name="threadId"></param>/// <returns></returns>static string TestMethodAsync(int callDuration, out int threadId){Stopwatch sw = new Stopwatch();sw.Start();Console.WriteLine("異步TestMethodAsync開始");for (int i = 0; i < 5; i++){ // 模擬耗時操作Thread.Sleep(callDuration);Console.WriteLine("TestMethodAsync:" + i.ToString());}sw.Stop();threadId = Thread.CurrentThread.ManagedThreadId;return string.Format("耗時{0}ms.", sw.ElapsedMilliseconds.ToString());}} }輸出
- 情況三:檢查BeginInvoke返回結果的狀態。
可以通過BeginInvoke的返回結果的IsCompleted屬性檢查異步是否完成。你可以在異步沒有完成的時候做其他的操作。
View Code
輸出
復制代碼
其它業務0
其它業務1
其它業務2
其它業務3
其它業務4
其它業務5
其它業務6
其它業務7
其它業務8
其它業務9
等待異步方法TestMethodAsync執行完成
異步TestMethodAsync開始
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
TestMethodAsync:0
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
TestMethodAsync:1
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
TestMethodAsync:2
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
TestMethodAsync:3
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
異步方法,running…
TestMethodAsync:4
異步方法,running…
關閉了WaitHandle句柄
Completed!
耗時5031ms.
復制代碼
- 情況四:通過在回調方法中。
如果需要在異步完成后需要做一些其他的操作,你可以在異步完成時執行一個回調方法。在該回調方法中做處理。
首先需要定義一個回調方法。
using System; using System.Diagnostics; using System.Threading;namespace BeginInvokeDemo {/// <summary>/// 委托必須和要調用的異步方法有相同的簽名/// </summary>/// <param name="callDuration">sleep時間</param>/// <param name="threadId">當前線程id</param>/// <returns></returns>public delegate string AsyncMethodCaller(int callDuration, out int threadId);class Program{/// <summary>/// 主函數/// </summary>/// <param name="args"></param>static void Main(string[] args){AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);int threadid = 0;//開啟異步操作IAsyncResult result = caller.BeginInvoke(1000, out threadid, callBackMethod, caller);for (int i = 0; i < 10; i++){Console.WriteLine("其它業務" + i.ToString());}//調用EndInvoke,等待異步執行完成Console.WriteLine("等待異步方法TestMethodAsync執行完成");//等待異步執行完畢信號//result.AsyncWaitHandle.WaitOne();//Console.WriteLine("收到WaitHandle信號");//通過循環不停的檢查異步運行狀態//while (result.IsCompleted==false)//{// Thread.Sleep(100);// Console.WriteLine("異步方法,running........");//}//異步結束,拿到運行結果//string res = caller.EndInvoke(out threadid, result);顯示關閉句柄//result.AsyncWaitHandle.Close();Console.WriteLine("關閉了WaitHandle句柄");//Console.WriteLine(res);Console.Read();}/// <summary>/// 異步方法回調方法,異步執行完畢,會回調該方法/// </summary>/// <param name="ar"></param>private static void callBackMethod(IAsyncResult ar){AsyncMethodCaller caller = ar.AsyncState as AsyncMethodCaller; string result = caller.EndInvoke(out int threadid, ar);Console.WriteLine("Completed!");Console.WriteLine(result);}/// <summary>/// 與委托對應的方法/// </summary>/// <param name="callDuration"></param>/// <param name="threadId"></param>/// <returns></returns>static string TestMethodAsync(int callDuration, out int threadId){Stopwatch sw = new Stopwatch();sw.Start();Console.WriteLine("異步TestMethodAsync開始");for (int i = 0; i < 5; i++){ // 模擬耗時操作Thread.Sleep(callDuration);Console.WriteLine("TestMethodAsync:" + i.ToString());}sw.Stop();threadId = Thread.CurrentThread.ManagedThreadId;return string.Format("耗時{0}ms.", sw.ElapsedMilliseconds.ToString());}} }總結
在項目中遇到的有參數又返回值的情況,如何在回調方法中拿到委托和傳遞的參數,當時卡這里了,后來查看情況四的情況,才得以解決。這里也再學習一下。
博客地址:http://www.cnblogs.com/wolf-sun/
#3樓 2018-05-23 16:56 LaTiao
不知道是我理解有問題還是別的情況,如果按照下面方法調用其實是需要等到函數執行完畢才會向下執行的,這種情況看起來并不是"異步",代碼:
輸出結果:
11
Main thread 9 does some work.
Test method begins:
The call executed on thread 10,with return value My call time was 3000…
22
我認為"22"應該打印在"Main thread 9 does some work."之后,才叫異步,比如代碼如下:
static AsyncMethodCaller caller; static void Main(string[] args) { caller = new AsyncMethodCaller(TestMethod); Console.WriteLine("1"); Test(); Console.WriteLine("2"); Console.Read(); } static void Test() { int dummy = 0; IAsyncResult result = caller.BeginInvoke(3000, out dummy, AsyncCallbackImpl, "The call executed on thread {0},with return value {1}."); Console.WriteLine(" *-*-* "); }static void AsyncCallbackImpl(IAsyncResult ar) { int dummy = 0;string re = caller.EndInvoke(out dummy, ar);Console.WriteLine(" *** " + re + ar.AsyncState); } static string TestMethod(int callDuration, out int threadId) { Console.WriteLine("Test method begins:"); //睡一會 模擬耗時操作 Thread.Sleep(callDuration); threadId = Thread.CurrentThread.ManagedThreadId; return string.Format("My call time was {0}.", callDuration.ToString()); }輸出結果如下:
1
--*
2
Test method begins:
*** My call time was 3000.The call executed on thread {0},with return value {1}.
不過有個問題就是這樣拿到的返回值很難用
#4樓 [樓主] 2018-05-25 11:31 wolfy
@ LaTiao
//等待WaitHandle信號
result.AsyncWaitHandle.WaitOne();
//調用EndInvoke方法,等待異步嗲用完成,并得到結果。
string returnValue = caller.EndInvoke(out threadId, result);
這種方式,通過waiteone阻塞了,是為了拿到異步的返回值。
一般endinvoke 用在beginInvoke的回調函數中。但,如果想在同一個方法中拿到返回值,可以采用這種方式。
支持(0) 反對(0)
#5樓 [樓主] 2018-05-25 13:18 wolfy
@ LaTiao
有可能,給的demo描述的不太清楚,我把demo,重新修改了下,更有助于理解,希望對你有所幫助
支持(0) 反對(0)
#6樓 2019-05-09 16:32 雨落憂傷
由上圖,可以看出,在BeginInvoke開啟異步執行方法,會先執行其他的業務。通過EndInvoke方法,阻塞直到異步執行完畢,才會執行EndInvoke之后的代碼。
請問 為什么會先 執行其他的業務 而不是一起執行?
如果 其他的業務 到 EndInvoke 之間 要花大量時間的處理
異步是不是會一直等待執行完了 再執行自己的異步方法?
總結
以上是生活随笔為你收集整理的c#中的BeginInvoke和EndEndInvoke 摘要的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 点了外卖无法支付!微信回应支付出现异常:
- 下一篇: C# 中的可变参数方法(VarArgs)