非同步程式模型 (Asynchronous Programming Model, APM) 是 .NET 1.0 時代的非同步標準,以 BeginMethod/EndMethod 配對與 IAsyncResult 介面為核心。雖已被 TAP (async/await) 取代,但理解 APM 有助於維護舊代碼、掌握非同步本質 (狀態機、回調、等待句柄)。本文從介面定義到委托實作完整入門。
public interface IAsyncResult
{
object AsyncState { get; } // 呼叫端傳入的 userState 物件
WaitHandle AsyncWaitHandle { get; } // 等待句柄,可用 WaitOne/WaitAll 同步阻塞
bool CompletedSynchronously { get; } // 是否同步完成 (未真正進入執行緒池)
bool IsCompleted { get; } // 作業是否完成
}
BeginMethod(參數, AsyncCallback callback, object state):啟動非同步作業,回傳 IAsyncResultEndMethod(IAsyncResult):結束作業,回傳結果或拋出例外,**必須呼叫一次**AsyncCallback:作業完成時呼叫的委托,參數為 IAsyncResult
var demo = new AsyncDemo("APM");
IAsyncResult ar = demo.BeginRun(null, null);
// 可做其他事...
string result = demo.EndRun(ar); // 這裡阻塞直到完成
Console.WriteLine(result);
IAsyncResult ar = demo.BeginRun(null, null);
if (ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
string result = demo.EndRun(ar); // 立即返回
Console.WriteLine(result);
}
else
{
Console.WriteLine("逾時");
// 注意:作業仍在背景執行,若需取消需自行實作取消邏輯
}
IAsyncResult ar = demo.BeginRun(null, null);
while (!ar.IsCompleted)
{
Console.Write(".");
Thread.Sleep(100); // 避免佔滿 CPU
}
string result = demo.EndRun(ar);
Console.WriteLine(result);
class Program
{
static AsyncDemo _demo = new AsyncDemo("Callback");
static void Main()
{
bool userState = false;
IAsyncResult ar = _demo.BeginRun(OnCompleted, userState);
Thread.Sleep(2000); // 模擬主執行緒做其他事,實際應用不應 Sleep
}
static void OnCompleted(IAsyncResult ar)
{
bool state = (bool)ar.AsyncState;
string result = _demo.EndRun(ar);
Console.WriteLine(state ? result : $"{result}, isn't it?");
}
}
任何同步方法皆可透過委托的 BeginInvoke/EndInvoke 獲得 APM 支援,無需手寫 IAsyncResult 實作。
public class AsyncDemo
{
private readonly string _name;
private readonly Func _syncMethod;
public AsyncDemo(string name)
{
_name = name;
_syncMethod = () => "My name is " + _name; // 同步邏輯
}
public IAsyncResult BeginRun(AsyncCallback callback, object state)
{
return _syncMethod.BeginInvoke(callback, state);
}
public string EndRun(IAsyncResult ar)
{
return _syncMethod.EndInvoke(ar);
}
}
優點:極簡單、自動處理執行緒池調度、支援所有四種等待策略。缺點:BeginInvoke/EndInvoke 在 .NET Core / .NET 5+ **不支援** (僅 .NET Framework),跨平台專案請改用 Task.Run。
InvalidOperationException。CompletedSynchronously == true 時,回調在呼叫 BeginMethod 的執行緒同步執行,若回調裡做耗時工作會阻塞發起者。EndMethod 拋出,**不會**在回調或 WaitHandle 自動拋出。
Func begin = (cb, s) => demo.BeginRun(cb, s);
Func end = ar => demo.EndRun(ar);
Task task = Task.Factory.FromAsync(begin, end, null);
string result = await task; // 現代 await 風格
此為遷移舊 APM API 到 TAP 的標準橋接方式。