首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 非同步程式設計基礎 (一):APM 模式 (IAsyncResult) 完整入門與委托實作

     編輯:關於C#

非同步程式模型 (Asynchronous Programming Model, APM) 是 .NET 1.0 時代的非同步標準,以 BeginMethod/EndMethod 配對與 IAsyncResult 介面為核心。雖已被 TAP (async/await) 取代,但理解 APM 有助於維護舊代碼、掌握非同步本質 (狀態機、回調、等待句柄)。本文從介面定義到委托實作完整入門。

IAsyncResult 介面定義


public interface IAsyncResult
{
    object AsyncState { get; }        // 呼叫端傳入的 userState 物件
    WaitHandle AsyncWaitHandle { get; } // 等待句柄,可用 WaitOne/WaitAll 同步阻塞
    bool CompletedSynchronously { get; } // 是否同步完成 (未真正進入執行緒池)
    bool IsCompleted { get; }         // 作業是否完成
}

APM 模式規範

  • BeginMethod(參數, AsyncCallback callback, object state):啟動非同步作業,回傳 IAsyncResult
  • EndMethod(IAsyncResult):結束作業,回傳結果或拋出例外,**必須呼叫一次**
  • AsyncCallback:作業完成時呼叫的委托,參數為 IAsyncResult

四種等待完成策略

1. 同步阻塞:呼叫 EndMethod 直接等待


var demo = new AsyncDemo("APM");
IAsyncResult ar = demo.BeginRun(null, null);
// 可做其他事...
string result = demo.EndRun(ar); // 這裡阻塞直到完成
Console.WriteLine(result);

2. WaitHandle 等待 (可設定逾時)


IAsyncResult ar = demo.BeginRun(null, null);
if (ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
    string result = demo.EndRun(ar); // 立即返回
    Console.WriteLine(result);
}
else
{
    Console.WriteLine("逾時");
    // 注意:作業仍在背景執行,若需取消需自行實作取消邏輯
}

3. 輪詢 IsCompleted (不阻塞主執行緒)


IAsyncResult ar = demo.BeginRun(null, null);
while (!ar.IsCompleted)
{
    Console.Write(".");
    Thread.Sleep(100); // 避免佔滿 CPU
}
string result = demo.EndRun(ar);
Console.WriteLine(result);

4. 回調委托 (非阻塞、事件驅動)


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?");
    }
}

委托實作 APM (零樣板碼)

任何同步方法皆可透過委托的 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。

APM 常見陷阱

  • 忘記呼叫 EndMethod:資源洩漏、非同步例外被吞沒、IAsyncResult 物件無法回收。
  • 重複呼叫 EndMethod:拋 InvalidOperationException。
  • 同步完成:CompletedSynchronously == true 時,回調在呼叫 BeginMethod 的執行緒同步執行,若回調裡做耗時工作會阻塞發起者。
  • 例外處理:例外在 EndMethod 拋出,**不會**在回調或 WaitHandle 自動拋出。
  • 委托 BeginInvoke 不支援 .NET Core:跨平台專案禁用。

從 APM 封裝為 Task (Task.Factory.FromAsync)


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 的標準橋接方式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved