開發多執行緒應用時,ThreadStart 委派不支援參數傳遞是常見痛點。當有大量相似執行緒(如 100 個連線掃描)僅參數不同時,若為每個執行緒寫獨立方法,程式碼將極度膨脹。本文提供一個以共用欄位暫存參數的實用解決方案。
// ThreadStart 無參數簽名
public delegate void ThreadStart();
// 導致必須寫大量重複方法
void ThreadMethod1() { /* 參數 A */ }
void ThreadMethod2() { /* 參數 B */ }
// ... 100 個方法?
public class PortScanner
{
private ServerBean _sharedParameter; // 類別層級暫存參數
private const int MAX_SERVERS = 100;
private ServerState[] _serverStates;
public void StartScanning()
{
for (int i = 0; i < MAX_SERVERS; i++)
{
// 1. 先將參數寫入共用欄位
_sharedParameter = (ServerBean)_serverStates[i];
// 2. 建立並啟動執行緒
var thread = new Thread(ScanPort);
thread.Start();
// 3. 關鍵:短暫睡眠確保執行緒讀取到參數
Thread.Sleep(10);
}
}
private void ScanPort()
{
// 執行緒進入點第一件事:複製參數到區域變數
var myParam = _sharedParameter;
// 後續使用 myParam 執行邏輯
// ...
}
}
| 風險 | 說明 | 改進方案 |
|---|---|---|
| 競態條件 | Sleep 時間不夠導致參數被覆寫 | 使用 ParameterizedThreadStart + thread.Start(param)(.NET 2.0+ 內建支援) |
| 記憶體可見性 | 多核心環境下欄位讀取可能不一致 | 加上 volatile 關鍵字或使用 Interlocked |
| 例外處理 | 執行緒內未捕獲例外會終止進程 | 在 ScanPort 內包裹 try-catch 並記錄日誌 |
// 使用 ParameterizedThreadStart(.NET 2.0 即支援)
var thread = new Thread(state =>
{
var param = (ServerBean)state;
ScanPort(param);
});
thread.Start(serverBean);
// 或使用 Task + Lambda(.NET 4.0+ 推薦)
Task.Run(() => ScanPort(serverBean));
// 並行處理大量工作
Parallel.ForEach(serverBeans, bean => ScanPort(bean));
總結:共用欄位方案在舊版 .NET 或特殊限制下可用,但現代開發應優先使用 ParameterizedThreadStart、Task 或 Parallel 等內建機制,更安全、簡潔且效能更佳。