多線程是 C# 開發的核心技能。本文透過一個完整的 WinForms 範例,展示如何建立、啟動、暫停與安全終止背景線程,並正確處理跨線程 UI 更新——這是初學者最容易踩坑的地方。
public partial class ThreadDemoForm : Form
{
private Thread _workerThread;
private int _maxCount;
private int _currentIndex = 0;
private readonly object _lockObj = new object();
public ThreadDemoForm() => InitializeComponent();
private void WorkerThreadProc()
{
try
{
while (true)
{
// 檢查是否需重置計數
if (_currentIndex >= _maxCount)
_currentIndex = 0;
// 關鍵:跨線程更新 UI 必須透過 Invoke/BeginInvoke
this.BeginInvoke(new Action(() =>
{
lblResult.Text = $"{_currentIndex} / {_maxCount}";
}));
_currentIndex++;
Thread.Sleep(50); // 模擬工作負載
}
}
catch (ThreadInterruptedException)
{
// 線程被中斷時的優雅退出
}
finally
{
// 清理資源
}
}
private void StartThread()
{
StopThread(); // 確保舊線程已停止
_maxCount = int.Parse(txtCount.Text);
_currentIndex = 0;
_workerThread = new Thread(WorkerThreadProc)
{
IsBackground = true // 設為背景線程,主視窗關閉時自動結束
};
_workerThread.Start();
}
private void StopThread()
{
if (_workerThread != null && _workerThread.IsAlive)
{
_workerThread.Interrupt(); // 發送中斷信號
_workerThread.Join(1000); // 等待線程結束(最多 1 秒)
_workerThread = null;
}
}
private void btnStart_Click(object sender, EventArgs e)
{
if (int.TryParse(txtCount.Text, out int count) && count > 0)
StartThread();
}
private void btnStop_Click(object sender, EventArgs e) => StopThread();
private void btnClose_Click(object sender, EventArgs e)
{
StopThread();
this.Close();
}
Windows Forms 控制項具有線程親和性,只能在建立它們的 UI 線程中存取。背景線程直接存取 lblResult.Text 會拋出 InvalidOperationException。BeginInvoke 將委派排入 UI 線程訊息佇列非同步執行,不阻塞背景線程。
| 方法 | 行為 | 推薦度 |
|---|---|---|
| Interrupt | 喚醒等待中的線程,拋出 ThreadInterruptedException,允許 finally 區塊執行 | ✅ 推薦 |
| Abort | 強制終止,可能導致資料損壞、鎖定未釋放,.NET Core 已移除 | ❌ 棄用 |
| CancellationToken | 現代協作式取消模式,配合 ThrowIfCancellationRequested | ✅ 最佳實踐 |
private CancellationTokenSource _cts;
private async void btnStart_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
int max = int.Parse(txtCount.Text);
try
{
for (int i = 0; !_cts.Token.IsCancellationRequested; i = (i + 1) % max)
{
lblResult.Text = $"{i} / {max}";
await Task.Delay(50, _cts.Token);
}
}
catch (OperationCanceledException) { /* 正常取消 */ }
}
private void btnStop_Click(object sender, EventArgs e) => _cts?.Cancel();
掌握線程建立、跨線程 UI 更新、優雅停止機制是多線程開發的基石。現代專案請優先採用 Task / async-await / CancellationToken 模式,程式碼更簡潔、除錯更容易、且避免傳統線程的常見陷阱。