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

.NET APM 異步模式擴展:CancellableTask 實現超時取消機制

     編輯:關於.NET

背景:APM 缺乏內建超時控制

.NET 早期的異步編程模型 (APM) —— BeginInvoke/EndInvoke、IAsyncResult、AsyncCallback —— 功能強大,但缺少標準化的超時/取消機制。開發者常需自行管理 Thread.Abort (不安全) 或輪詢標誌位。本文介紹 CancellableTask 類,在保持 APM 風格的前提下,提供可配置超時與自定義取消回調的擴展。

設計目標

  1. 保持 APM 風格:BeginInvoke(arg, callback, state, timeoutMs)、EndInvoke、IAsyncResult 完全兼容。
  2. 雙重取消策略:預設 Thread.Abort (兼容舊代碼) + 支援自定義 CancelCallback (協作式取消,安全)。
  3. 異常正確傳播:工作異常在 EndInvoke 拋出;超時中止拋出 ThreadAbortException。

使用示例

案例 1:預設超時中止


var task = new CancellableTask(Work);
var arg = new State { Loop = 20, Stop = false };
IAsyncResult ar = task.BeginInvoke(arg, ar => Console.WriteLine("Callback"), null, 10_000); // 10秒超時
ar.AsyncWaitHandle.WaitOne();
try {
    object result = task.EndInvoke(ar);
    Console.WriteLine("Result: " + result);
} catch (ThreadAbortException) { Console.WriteLine("Thread Aborted"); }
catch (Exception ex) { Console.WriteLine(ex); }

案例 2:自定義協作式取消


var task = new CancellableTask(Work, Cancel); // 傳入取消回調
var arg = new State { Loop = 20, Stop = false };
task.BeginInvoke(arg, ar => {
    try { Console.WriteLine("Result: " + task.EndInvoke(ar)); }
    catch (ThreadAbortException) { Console.WriteLine("Aborted"); }
    catch (Exception ex) { Console.WriteLine(ex); }
}, arg, 10_000);

// 取消邏輯:設置標誌位,Work 內部輪詢
static void Cancel(object state) => ((State)state).Stop = true;

static object Work(object arg) {
    var s = (State)arg;
    for (int i = 0; i < s.Loop; i++) {
        if (s.Stop) break; // 協作式退出
        Console.WriteLine(i); Thread.Sleep(1000);
    }
    return i;
}

核心實現原理

Wrapper 委托包裝


public IAsyncResult BeginInvoke(object arg, AsyncCallback callback, object state, int timeout)
{
    wrapper = delegate(object argv) {
        var e = new AutoResetEvent(false);
        try {
            var waitState = new TimeoutState(Thread.CurrentThread, state);
            // 註冊等待:timeout 到期觸發 WaitOrTimeout
            ThreadPool.RegisterWaitForSingleObject(e, WaitOrTimeout, waitState, timeout, true);
            return workCallback(argv); // 執行真實工作
        } finally { e.Set(); } // 工作提前結束,觸發已註冊等待的非超時分支
    };
    return wrapper.BeginInvoke(arg, callback, state); // 使用標準委托 APM
}

超時回調處理


protected void WaitOrTimeout(object state, bool isTimeout)
{
    if (isTimeout) {
        var ts = (TimeoutState)state;
        if (cancelCallback != null) cancelCallback(ts.state); // 自定義取消
        else ts.thread.Abort(); // 預設強制中止
    }
    // 非超時 (isTimeout=false) 什麼都不做,工作正常完成
}

關鍵類別結構


public class CancellableTask {
    public delegate object WorkCallback(object arg);
    public delegate void CancelCallback(object state);

    protected class TimeoutState { internal Thread thread; internal object state; ... }
    protected WorkCallback workCallback;
    protected CancelCallback cancelCallback;
    protected WorkCallback wrapper; // 包裝後的委托

    public CancellableTask(WorkCallback work) { this.workCallback = work; }
    public CancellableTask(WorkCallback work, CancelCallback cancel) : this(work) { this.cancelCallback = cancel; }

    public IAsyncResult BeginInvoke(object arg, AsyncCallback callback, object state, int timeout) { ... }
    public object EndInvoke(IAsyncResult result) => wrapper.EndInvoke(result);
    protected void WaitOrTimeout(object state, bool isTimeout) { ... }
}

關鍵技術點解析

技術點說明
ThreadPool.RegisterWaitForSingleObject高效的等待註冊,由執行緒池監控 WaitHandle,超時在執行緒池執行緒上觸發回調,無需額外計時器執行緒。
AutoResetEvent 作為觸發器工作正常結束時 e.Set(),喚醒等待器,isTimeout=false;超時則由執行緒池直接觸發 isTimeout=true。
Wrapper 委托捕獲wrapper 字段保存包裝委托,供 EndInvoke 調用,保證 APM 鏈路完整。
異常處理工作異常在 EndInvoke 重拋;Thread.Abort 產生 ThreadAbortException 同樣在 EndInvoke 拋出;取消回調異常被吞噬 (避免崩潰監控執行緒)。

安全性警示與最佳實踐

⚠️ Thread.Abort 危險:可能導致鎖未釋放、非托管資源洩漏、應用程式域不穩定。生產環境強制要求提供 CancelCallback 實現協作式取消。

協作式取消模板


static void SafeWork(object arg) {
    var state = (MyState)arg;
    for (int i = 0; i < state.MaxIterations; i++) {
        if (state.CancellationToken.IsCancellationRequested) break; // 或輪詢 bool Stop
        // 執行可中斷的小步驟
        DoStep(i);
    }
}

static void SafeCancel(object state) => ((MyState)state).CancellationToken.Cancel(); // 或 state.Stop = true;

對比:CancellableTask vs 現代 CancellationToken

特性CancellableTask (APM)CancellationToken (TAP)
適用模型委托 APM (Begin/EndInvoke)Task/async-await (TAP)
取消機制超時自動觸發 + 可選回調統一 CancellationTokenSource/Token
協作式取消手動輪詢標誌位/Token原生支援 ThrowIfCancellationRequested
組合性差 (單獨任務)優 (Token.Register、LinkedTokenSource)
推薦場景維護舊 APM 代碼、無法升級 TAP所有新代碼、.NET 4.5+

總結

CancellableTask 為遺留 APM 代碼提供了實用的超時擴展,核心利用 ThreadPool.RegisterWaitForSingleObject 實現高效計時監控。但新專案應直接採用 TAP (Task/async-await) + CancellationToken,享受統一、可組合、類型安全的現代取消模型。本方案僅作為存量系統平滑遷移的過渡工具。

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