以下範例模擬「逐字元輸出文字並延遲 1 秒」的耗時操作,對比單執行緒阻塞 UI 與多執行緒並行處理的差異。
public delegate void WriteTextCallback(char ch); // 跨執行緒更新 UI 的委派
public void Write(string text, WriteTextCallback callback)
{
foreach (char c in text)
{
callback(c); // 呼叫委派更新對應 TextBox
// 模擬耗時操作:忙碌等待 1 秒(實務請用 Task.Delay)
DateTime now = DateTime.Now;
while (now.AddSeconds(1) > DateTime.Now) { }
}
}
private void btnStart_Click(object sender, EventArgs e)
{
// 勾選 CheckBox1 才執行
if (checkBox1.Checked)
{
textBox1.Clear();
// 直接在 UI 執行緒呼叫 Write,會凍結整個視窗
Write(textBox3.Text, c => textBox1.AppendText(c.ToString()));
}
if (checkBox2.Checked)
{
textBox2.Clear();
// 必須等待第一個 Write 完全結束才會執行
Write(textBox3.Text, c => textBox2.AppendText(c.ToString()));
}
}
現象: 視窗標題列顯示「未回應」,無法移動、無法點擊其他控制項,兩個 TextBox 依序輸出而非同時。
private void btnStart_Click(object sender, EventArgs e)
{
// 允許跨執行緒存取控制項(教學用,生產環境請用 Invoke/BeginInvoke)
CheckForIllegalCrossThreadCalls = false;
if (checkBox1.Checked)
{
textBox1.Clear();
var t1 = new Thread(() => Write(textBox3.Text, c => textBox1.AppendText(c.ToString())));
t1.Start();
}
if (checkBox2.Checked)
{
textBox2.Clear();
var t2 = new Thread(() => Write(textBox3.Text, c => textBox2.AppendText(c.ToString())));
t2.Start();
}
}
現象: 視窗保持流暢可操作,兩個 TextBox 同時逐字元輸出,互不干擾。
| 技術點 | 單執行緒 | 多執行緒 |
|---|---|---|
| UI 回應性 | 完全凍結 | 完全正常 |
| 執行順序 | 嚴格循序 | 並行交錯 |
| 資源爭用 | 無 | 需同步機制 |
| 程式碼複雜度 | 低 | 中高 |
| 適用場景 | 簡單、快速、CPU 密集任務 | I/O 密集、長時間運算、需保持 UI 回應 |
上述範例使用 Thread 類別與 CheckForIllegalCrossThreadCalls = false 屬於教學示範手法。實際專案建議採用:
await Task.Run(() => DoWork()) 自動處理執行緒池排程與上下文捕捉。Invoke。Thread.Abort() 等危險作法。多執行緒是把雙面刃:用得好能讓應用程式「動起來」,用不好會引入難以重現的競態條件與死結。建議遵循「預設單執行緒、證明需要時再並行」原則,優先使用高階抽象而非手動操作 Thread。