先看一個經典的同步 Hello World:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("1,Thread ID:#{0}, IsPoolThread?{1}",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
AsyncTest test = new AsyncTest();
string val = test.Hello("Andy Huang");
Console.WriteLine(val);
Console.ReadLine();
}
}
public class AsyncTest
{
public string Hello(string name)
{
Console.WriteLine("2,Thread ID:#{0}, IsPoolThread?{1}",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
return "Hello:" + name;
}
}
輸出顯示主程式與 Hello 方法在**同一個非執行緒池執行緒**上執行(IsPoolThread=False),這就是典型的同步阻塞呼叫。
透過委派的 BeginInvoke / EndInvoke 即可將工作排入執行緒池:
// 1. 宣告委派
public delegate string AsyncEventHandler(string name);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("1,Thread ID:#{0}, IsPoolThread?{1}",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
AsyncTest test = new AsyncTest();
AsyncEventHandler async = test.Hello; // 指派目標方法
IAsyncResult result = async.BeginInvoke("Andy Huang", null, null); // 非同步啟動
string val = async.EndInvoke(result); // 等待並取得結果
Console.WriteLine(val);
Console.ReadLine();
}
}
public class AsyncTest
{
public string Hello(string name)
{
Console.WriteLine("2,Thread ID:#{0}, IsPoolThread?{1}",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
return "Hello:" + name;
}
}
輸出關鍵差異:2,Thread ID:#10, IsPoolThread?True —— 方法現在在**執行緒池執行緒**上跑,主執行緒在 EndInvoke 處阻塞等待結果。
使用 IL Disassembler (ildasm) 或 Reflector 反編譯,發現委派實際上是一個繼承 MulticastDelegate 的 sealed class:
public sealed class AsyncEventHandler : MulticastDelegate
{
public AsyncEventHandler(object @object, IntPtr method) { }
public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object) { }
public virtual string EndInvoke(IAsyncResult result) { }
public virtual string Invoke(string name) { }
}
BeginInvoke 多出 AsyncCallback 與 object state,回傳 IAsyncResult;EndInvoke 接收 IAsyncResult 並回傳結果。適用於**執行時間較長**的作業,避免阻塞主執行緒(UI 凍結、請求排隊):
FileStream, Socket, HttpWebRequest)MessageQueue).NET Framework 在 System.IO、System.Net、System.Data、System.Messaging、ASP.NET 等命名空間廣泛內建非同步版本(BeginXxx/EndXxx 或 Task-based),開發者可直接使用。
本文建立了最基礎的認知:
BeginInvoke/EndInvoke。EndInvoke 必須呼叫,否則資源洩漏。下一篇將深入探討 IAsyncResult 狀態機制、回呼模式、AsyncState 傳遞與異常處理細節。