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

.NET 後台執行緒優雅取消模式完整指南

     編輯:ASP.NET 基礎

前言:為什麼需要優雅取消?

多執行緒應用中,前景 UI 執行緒結束時,背景工作執行緒若仍在運行,會導致進程無法正常退出,甚至造成資源洩漏(如記憶體資料未寫入磁碟)。.NET 提供多種機制,本文從淺入深解析四大模式優劣與適用場景。

一、IsBackground 屬性:最簡單的「隨進程而亡」

var worker = new Thread(DoWork) { IsBackground = true };
worker.Start();
  • 優點:程式碼極簡,進程結束時自動終止所有背景執行緒。
  • 缺點:執行緒被強制終止,不會拋出例外、不會執行 finally 區塊,資料可能遺失。
  • 適用場景:無狀態、可丟棄的工作(如 Socket 監聽、心跳偵測),不需資料持久化。

二、Thread.Abort():暴力終止的反模式


try { /* 長時間工作 */ }
catch (ThreadAbortException)
{
    // 僅能在這裡做極簡單清理
    Thread.ResetAbort(); // 甚至可阻止中止
}
  • 缺點:不可預知執行到哪一行,清理邏輯極難撰寫;可能導致鎖未釋放、非受控資源洩漏;.NET Core / .NET 5+ 已標記過時。
  • 結論:強烈不建議 用於生產環境。

三、輪詢模式:CancellationToken 協作式取消標準

.NET Framework 4.0 引入 CancellationTokenSource / CancellationToken,成為取消模式標準。

基本範例:長時間計算任務


public class CancellationDemo
{
    public static void Main()
    {
        var cts = new CancellationTokenSource();
        var worker = new Thread(DoWork);
        worker.Start(cts.Token);

        Console.WriteLine("按 'c' 取消執行緒...");
        while (Console.ReadKey().KeyChar != 'c') { }
        cts.Cancel();
        worker.Join();
    }

    static void DoWork(object state)
    {
        var token = (CancellationToken)state;
        while (true)
        {
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("執行緒收到取消請求,正在清理資源...");
                // 清理:寫入檔案、釋放鎖、關閉連線
                break;
            }
            Thread.SpinWait(500000); // 模擬計算
            Console.WriteLine("持續工作中...");
        }
    }
}

關鍵點

  • 工作迴圈中主動檢查 token.IsCancellationRequested。
  • 取消時執行確定性清理(finally 區塊、using 區塊正常運作)。
  • cts.Token 可傳遞給多個執行緒、非同步方法,實現群組取消。

四、取消阻塞執行緒:WaitHandle.WaitAny 多路複用

當執行緒因 Thread.Sleep、AutoResetEvent.WaitOne、Socket.Receive 等阻塞時,無法輪詢 Token。解決方案:利用 CancellationToken.WaitHandle 與業務事件同步等待。

生產者-消費者模式完整範例


public class BlockingCancellationDemo
{
    private static int _sharedValue;

    public static void Main()
    {
        var valueReady = new AutoResetEvent(false);
        var cts = new CancellationTokenSource();
        var state = new { ValueReady = valueReady, Token = cts.Token };

        new Thread(Consumer).Start(state);
        new Thread(Producer).Start(state);

        Console.WriteLine("按 'c' 取消所有執行緒...");
        while (Console.ReadKey().KeyChar != 'c') { }
        cts.Cancel(); // 觸發 Token.WaitHandle
    }

    static void Producer(object state)
    {
        dynamic s = state;
        var rand = new Random();
        while (!s.Token.IsCancellationRequested)
        {
            _sharedValue = rand.Next();
            Console.WriteLine($"\r\n[生產] 產生值: {_sharedValue}");
            s.ValueReady.Set(); // 通知消費者
            Thread.Sleep(500);
        }
        Console.WriteLine("[生產] 執行緒已取消。");
    }

    static void Consumer(object state)
    {
        dynamic s = state;
        // 同時等待:資料就緒事件 或 取消事件
        var handles = new WaitHandle[] { s.ValueReady, s.Token.WaitHandle };

        while (true)
        {
            int index = WaitHandle.WaitAny(handles);
            if (index == 0) // 資料就緒
            {
                Console.WriteLine($"[消費] 處理值: {_sharedValue}");
            }
            else if (index == 1) // 取消事件
            {
                Console.WriteLine("[消費] 收到取消信號,退出迴圈。");
                break;
            }
        }
    }
}

核心原理

  • CancellationToken.WaitHandle 內部是一個 ManualResetEvent,cts.Cancel() 會將其置為 Signaled。
  • WaitHandle.WaitAny 只要任一 Handle 觸發即返回,並回傳索引,讓程式碼判斷是業務事件還是取消事件。
  • 同樣適用於 Semaphore、Mutex、Task 等同步原語。

總結與最佳實踐建議

模式優點缺點推薦度
IsBackground零成本資料遺失風險極高⭐ 僅限可丟棄工作
Thread.Abort即時生效不可靠、危險、已過時❌ 禁止使用
CancellationToken 輪詢標準、安全、可組合需修改工作迴圈、無法中斷阻塞⭐⭐⭐⭐⭐ 首選
WaitHandle.WaitAny支援阻塞場景程式碼稍複雜⭐⭐⭐⭐ 阻塞場景必備

核心原則:協作大於強制。設計長時間運行的背景工作時,請從一開始就引入 CancellationToken,並妥善處理阻塞呼叫的取消邏輯,才能構建健壯、可維護的多執行緒應用。

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