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

MultiBackgroundWorker:支援多任務並發的增強型異步組件

     編輯:關於.NET

前言:BackgroundWorker 的侷限與突破

.NET 2.0 引入的 BackgroundWorker 簡化了單任務異步,但面對「同時下載多個檔案、並行計算多筆報表」等多任務場景,開發者只能建立多個實例,管理極不便利。MultiBackgroundWorker 在單一實例內集中管理多個異步任務生命週期,保留熟悉的事件模型 (DoWork/ProgressChanged/RunWorkerCompleted),並新增 taskId 區分任務。

核心 API 設計

成員簽名說明
RunWorkerAsyncvoid RunWorkerAsync(object taskId, object argument)啟動任務,taskId 必須唯一
DoWorkevent MultiDoWorkEventHandler DoWork背景執行邏輯,參數含 TaskId、Argument
ReportProgressvoid ReportProgress(object taskId, int %, object userState)回報進度,觸發 ProgressChanged
ProgressChangedevent MultiProgressChangedEventHandler ProgressChangedUI 執行緒安全更新介面
RunWorkerCompletedevent MultiRunWorkerCompletedEventHandler RunWorkerCompleted任務結束 (正常/取消/異常)
CancelAsyncvoid CancelAsync(object taskId)請求取消指定任務
TaskCanceledbool TaskCanceled(object taskId)查詢任務是否已取消

WinForm 範例:多任務求和計算

1. 介面佈局

多任務演示界面

  • ListView:顯示 TaskId、最大加數、進度%、工作執行緒 Id、結果。
  • 三按鈕:Start (新增任務)、Cancel (取消勾選)、Remove (移除完成項)。

2. 初始化與事件註冊


_multiWorker = new MultiBackgroundWorker();
_multiWorker.DoWork += OnDoWork;
_multiWorker.ProgressChanged += OnProgressChanged;
_multiWorker.RunWorkerCompleted += OnRunWorkerCompleted;

3. 啟動任務


private void btnStart_Click(object sender, EventArgs e)
{
    int n = new Random().Next(50, 100);
    string taskId = Guid.NewGuid().ToString();
    AddListViewItem(taskId, n); // 先加入 UI 佔位
    _multiWorker.RunWorkerAsync(taskId, n);
}

4. 背景運算邏輯 (DoWork)


void OnDoWork(object sender, MultiDoWorkEventArgs e)
{
    int n = (int)e.Argument;
    object taskId = e.TaskId;
    e.Result = ComputeSum(n, taskId, (MultiBackgroundWorker)sender, e);
}

long ComputeSum(int n, object taskId, MultiBackgroundWorker worker, MultiDoWorkEventArgs e)
{
    long sum = 0;
    for (int i = 1; i <= n; i++)
    {
        if (worker.TaskCanceled(taskId)) { e.Cancel = true; break; }
        sum += i;
        Thread.Sleep(1000); // 模擬耗時
        int pct = (int)((float)i / n * 100);
        worker.ReportProgress(taskId, pct, Thread.CurrentThread.ManagedThreadId);
    }
    return sum;
}

5. 進度回報 (UI 執行緒安全)


void OnProgressChanged(object sender, MultiProgressChangedEventArgs e)
{
    UpdateListViewItem((string)e.TaskId, e.ProgressPercentage, (int)e.UserState);
}

6. 完成處理


void OnRunWorkerCompleted(object sender, MultiRunWorkerCompletedEventArgs e)
{
    string taskId = (string)e.TaskId;
    ListViewItem item;
    if (e.Error != null) item = UpdateListViewItem(taskId, "Error");
    else if (e.Cancelled) item = UpdateListViewItem(taskId, "Canceled");
    else item = UpdateListViewItem(taskId, ((long)e.Result).ToString());
    item.Tag = null; // 清除 taskId,防止重複取消
}

7. 取消任務


private void btnCancel_Click(object sender, EventArgs e)
{
    foreach (ListViewItem lvi in listView1.SelectedItems)
        if (lvi.Tag != null)
            _multiWorker.CancelAsync(lvi.Tag);
    btnCancel.Enabled = false;
}

實現原理深度解析

1. 任務生命週期容器:HybridDictionary


private HybridDictionary _userStateToLifetime = new HybridDictionary();
// key: taskId, value: AsyncOperation

線程安全鎖定 SyncRoot,確保多執行緒並發存取安全。

2. RunWorkerAsync 內部流程


public virtual void RunWorkerAsync(object taskId, object argument)
{
    var asyncOp = AsyncOperationManager.CreateOperation(taskId);
    lock (_userStateToLifetime.SyncRoot)
    {
        if (_userStateToLifetime.Contains(taskId))
            throw new ArgumentException("Task ID must be unique");
        _userStateToLifetime[taskId] = asyncOp;
    }
    _threadStart.BeginInvoke(taskId, argument, null, null);
}
  • 建立 AsyncOperation 綁定任務上下文 (自動捕獲 UI SynchronizationContext)。
  • 異步委托 _threadStart 指向 WorkerThreadStart。

3. WorkerThreadStart:核心執行迴路


private void WorkerThreadStart(object taskId, object argument)
{
    object result = null; Exception error = null; bool cancelled = false;
    var asyncOp = GetAsyncOperation(taskId);
    try
    {
        var e = new MultiDoWorkEventArgs(taskId, argument);
        OnDoWork(e);
        if (e.Cancel) cancelled = true;
        else result = e.Result;
    }
    catch (Exception ex) { error = ex; }

    if (!TaskCanceled(taskId)) // 未被外部取消
        lock (_userStateToLifetime.SyncRoot)
            _userStateToLifetime.Remove(taskId); // 結束生命週期

    var ee = new MultiRunWorkerCompletedEventArgs(taskId, result, error, cancelled);
    asyncOp.PostOperationCompleted(_onCompletedDelegate, ee);
}

4. 進度回報與完成通知的執行緒切換魔法

  • ReportProgress → AsyncOperation.Post → ProgressChanged (UI 執行緒)。
  • RunWorkerCompleted → AsyncOperation.PostOperationCompleted (UI 執行緒)。

AsyncOperation 內部持有 SynchronizationContext,自動將委托排程至建立時的執行緒 (通常是 UI 執行緒),完美解決跨執行緒 UI 存取問題。

5. 取消與狀態查詢


public bool TaskCanceled(object taskId) => _userStateToLifetime[taskId] == null;

public void CancelAsync(object taskId)
{
    var asyncOp = _userStateToLifetime[taskId] as AsyncOperation;
    if (asyncOp != null)
        lock (_userStateToLifetime.SyncRoot)
            _userStateToLifetime.Remove(taskId); // 移除即視為取消請求
}

DoWork 內部定期檢查 worker.TaskCanceled(taskId) 並設定 e.Cancel = true 實現協作式取消。

總結與擴充建議

  • 單實例管理多任務,API 與 BackgroundWorker 高度一致,學習成本極低。
  • 可擴充:任務優先級佇列、全域並發度限制 (SemaphoreSlim)、任務依賴圖 (DAG)。
  • 原始碼開源,歡迎貢獻 PR!
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved