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方法中傳遞該委託,在回調方法中調用該委託的EenInvoke方法。
注意
無論你怎麼使用,都必須調用EndInvoke方法結束你的異步調用。
下面通過模擬一個耗時的操作,實現上面說的那四種情況。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Wolfy.AsyncDemo { /// <summary> /// 委託必須和要調用的異步方法有相同的簽名 /// </summary> /// <param name="callDuration"></param> /// <param name="threadId"></param> /// <returns></returns> public delegate string AsyncMethodCaller(int callDuration, out int threadId); class Program { static void Main(string[] args) { } 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()); } } }
情況一:通過EndInovke阻塞線程,直到異步調用結束。
static void Main(string[] args) { int threadId; AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null); //調用EndInvoke方法,等待異步嗲用完成,並得到結果。 string returnValue = caller.EndInvoke(out threadId, result); Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue); Console.Read(); }
結果
情況二:通過WaitHandle屬性阻塞線程。
你可以獲得BeginInvoke的返回值的WaitHandle,並使用它的AsyncWaitHanlde屬性。WaitHandle信號異步完成時,你可以通過調用WaitOne方法等待。
如果你使用WaitHandle,你可以在之前或者異步調用完成後進行其他的操作。但之後必須使用EndInvoke檢查結果。
注意
當你調用EndInvoke方法時,等待句柄並不會自動關閉。如果你釋放等待處理的所有引用,當垃圾回收等待句柄是,系統資源將被釋放。一旦你完成使用等待句柄,通過WaitHandle的close方法,一次性顯示關閉,這時的垃圾回收效率更高。
static void Main(string[] args) { int threadId; AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId); //等待WaitHandle信號 result.AsyncWaitHandle.WaitOne(); //調用EndInvoke方法,等待異步嗲用完成,並得到結果。 string returnValue = caller.EndInvoke(out threadId, result); //關閉等待句柄 result.AsyncWaitHandle.Close(); Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue); Console.Read(); }
情況三:檢查BeginInvoke返回結果的狀態。
可以通過BeginInvoke的返回結果的IsCompleted屬性檢查異步是否完成。你可以在異步沒有完成的時候做其他的操作。
static void Main(string[] args) { int threadId; AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId); while (result.IsCompleted == false) { Thread.Sleep(250); Console.WriteLine("*"); } //調用EndInvoke方法,等待異步嗲用完成,並得到結果。 string returnValue = caller.EndInvoke(out threadId, result); //關閉等待句柄 result.AsyncWaitHandle.Close(); Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue); Console.Read(); }
情況四:通過在回調方法中。
如果需要在異步完成後需要做一些其他的操作,你可以在異步完成時執行一個回調方法。在該回調方法中做處理。
首先需要定義一個回調方法。
static void Main(string[] args) { AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); // The threadId parameter of TestMethod is an out parameter, so // its input value is never used by TestMethod. Therefore, a dummy // variable can be passed to the BeginInvoke call. If the threadId // parameter were a ref parameter, it would have to be a class- // level field so that it could be passed to both BeginInvoke and // EndInvoke. int dummy = 0; // Initiate the asynchronous call, passing three seconds (3000 ms) // for the callDuration parameter of TestMethod; a dummy variable // for the out parameter (threadId); the callback delegate; and // state information that can be retrieved by the callback method. // In this case, the state information is a string that can be used // to format a console message. IAsyncResult result = caller.BeginInvoke(3000, out dummy, TestMethodCallback, "The call executed on thread {0},with return value {1}."); Console.Read(); }
總結
在項目中遇到的有參數又返回值的情況,如何在回調方法中拿到委託和傳遞的參數,當時卡這裏了,後來查看情況四的情況,才得以解決。這裏也再學習一下。