原文:https://www.cnblogs.com/wolf-sun/p/5675791.html
正文
.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方法結束你的異步調用。
下面通過模擬一個耗時的操作,實現上面說的那四種情況。
情況一:通過EndInovke阻塞線程,直到異步調用結束。
複製代碼
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執行完成");
string res = caller.EndInvoke(out threadid, result);
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開啓異步執行方法,會先執行其他的業務。通過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屬性檢查異步是否完成。你可以在異步沒有完成的時候做其他的操作。
複製代碼
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, null, null);
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("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());
}
}
}
複製代碼
輸出
複製代碼
其它業務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());
}
}
}
複製代碼