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

C# 多執行緒爬蟲架構設計:線程池控制、去重、優雅啟停與資料庫協調

     編輯:關於C#

單執行緒爬蟲的效能瓶頸

  1. 串行化:分析 HTML 與下載網頁無法並行,網路頻寬與 CPU 交替閒置
  2. 單連線下載:無法利用多連線並行下載(如迅雷多線程),受限於單一 TCP 連線吞吐量

多執行緒架構核心設計

1. 固定大小線程池


class Crawler {
    private readonly Thread[] _workers;
    private readonly ThreadStart _work;
    public Crawler(int threadCount) {
        _workers = new Thread[threadCount];
        _work = DownloadWorker;
        for (int i = 0; i < threadCount; i++) {
            _workers[i] = new Thread(_work) { IsBackground = true };
            _workers[i].Start();
        }
    }
}

2. URL 去重與分派:資料庫協調

建立 UrlQueue 資料表:


CREATE TABLE UrlQueue (
    Url NVARCHAR(500) PRIMARY KEY,
    Content NVARCHAR(MAX) NULL,
    AttemptCount INT DEFAULT 0,
    WorkerId INT DEFAULT -1,  -- -1 表示未認領
    Status TINYINT DEFAULT 0  -- 0=待處理, 1=處理中, 2=成功, 3=失敗
);

工作者認領邏輯(交易確保原子性):


string ClaimUrl(int workerId) {
    using (var tran = conn.BeginTransaction()) {
        var cmd = conn.CreateCommand();
        cmd.Transaction = tran;
        cmd.CommandText = @"
            UPDATE TOP(1) UrlQueue
            SET WorkerId = @wid, AttemptCount = AttemptCount + 1, Status = 1
            OUTPUT INSERTED.Url
            WHERE Status = 0 AND AttemptCount < @maxRetry;
        ";
        cmd.Parameters.AddWithValue("@wid", workerId);
        cmd.Parameters.AddWithValue("@maxRetry", 3);
        var url = cmd.ExecuteScalar()?.ToString();
        tran.Commit();
        return url;
    }
}

3. 下載工作者主迴圈


void DownloadWorker() {
    int workerId = Thread.CurrentThread.ManagedThreadId;
    int emptyCycles = 0;
    while (true) {
        string url = ClaimUrl(workerId);
        if (string.IsNullOrEmpty(url)) {
            if (++emptyCycles > 10) break; // 連續 10 次無任務視為結束
            Thread.Sleep(1000);
            continue;
        }
        emptyCycles = 0;
        try {
            string html = Download(url);
            ParseAndEnqueueNewUrls(html);
            UpdateUrlStatus(url, html, success: true);
        } catch {
            UpdateUrlStatus(url, null, success: false);
        }
    }
}

4. 優雅啟動與停止


public void Start() { /* 已在建構函式啟動 */ }
public void Stop() {
    foreach (var t in _workers) {
        if (t.IsAlive) t.Abort(); // 或使用 CancellationToken 協作式取消
    }
}

關鍵改進方向(留給讀者優化)

  • 改用 ThreadPool / Task / Parallel.ForEach / Dataflow (TPL Dataflow) 替代手動 Thread
  • 引入 SemaphoreSlim 限制並發下載數,避免對目標站點造成過大壓力
  • 使用 HttpClient 單例 + Polly 重試策略
  • 去重改用 Bloom Filter + Redis / 記憶體 HashSet,減少資料庫壓力
  • 遵守 robots.txt、設定 User-Agent、間隔請求
  • 異常分類:暫時性(重試)vs 永久性(記錄跳過)
  • 斷點續爬:定期持久化隊列狀態

總結

文章展示了以資料庫為協調中心的多執行緒爬蟲雛形,解決了線程數控制、URL 去重認領、工作窮盡判斷、優雅停止四大核心問題。現代開發建議升級為 TPL Dataflow 或 Channel 管道模式,程式碼更簡潔、可維護性更高。

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