1. 介紹
假設執行緒就像排隊買冰淇淋的人。有些人安靜地排隊,有些人會大聲喊("我快要趕不上火車了,讓我先!"),店員好像能聽到每個人,但有時會決定先服務某些人——例如抱著哭嬰的媽媽。執行緒的優先順序大概就是這種情況。
在 C#(更精確地說,在 .NET)中,每個執行緒都有一個優先順序——告訴作業系統這個執行緒相對於其他執行緒有多「重要」。這不是對 OS 的硬性命令(沒人保證最高優先的執行緒永遠會得到所有資源),但通常高優先順序的執行緒會獲得較多的 CPU 時間。
這在實務上在哪裡有用?
- 在使用者介面:例如畫面更新不應該被低優先的耗時計算卡住。
- 在遊戲裡:渲染幀比背景地圖生成更重要。
- 在需要即時反應的任務:控制硬體、訊號處理等等。
2. 執行緒優先順序:運作方式
在 C# 中操作執行緒優先順序很簡單——Thread 物件有個屬性 Priority。它的值來自列舉 ThreadPriority:
| 值 | 描述 |
|---|---|
|
最低的優先順序 |
|
低於一般 |
|
一般(預設) |
|
高於一般 |
|
最高的優先順序 |
程式範例:
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread lowPriorityThread = new Thread(PrintLowPriority);
Thread highPriorityThread = new Thread(PrintHighPriority);
lowPriorityThread.Priority = ThreadPriority.Lowest;
highPriorityThread.Priority = ThreadPriority.Highest;
lowPriorityThread.Start();
highPriorityThread.Start();
}
static void PrintLowPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("低優先順序: " + i);
Thread.Sleep(10); // 加點延遲,好看出差別
}
}
static void PrintHighPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("高優先順序: " + i);
Thread.Sleep(10);
}
}
}
注意
在實務上,特別是在現代多核系統與受管理的 .NET 環境裡,優先順序只是給作業系統的建議。OS(與 .NET)會嘗試分配更多時間給高優先順序的執行緒,但沒有硬性保證。例如,如果一個執行緒把 CPU 全耗完且設為 Highest,使用者介面可能會卡住,其他執行緒也會受影響。
3. 更改執行緒優先順序
執行緒與優先順序
graph LR
A[執行緒 1 - Lowest] -->|較少的 CPU 時間| OS(作業系統)
B[執行緒 2 - Normal] --> OS
C[執行緒 3 - Highest] -->|較多的 CPU 時間| OS
OS --> CPU(處理器)
為什麼要改優先順序?
看起來好像可以把所有東西都設成 Highest,但這是壞主意!想像如果商店裡所有顧客同時大喊 "先服務我!"。
只有在有正當理由時才改變優先順序:
- 寫日誌的背景執行緒——可以設成 BelowNormal 或 Lowest。
- 處理使用者輸入的執行緒——設成 AboveNormal。
- CPU 密集但不那麼即時的任務——低優先順序。
小技巧:如果應用程式變慢,檢查是否有「囂張」的高優先執行緒在搶資源,影響其他工作。
4. .NET 裡的執行緒類別(類型)
在 C# 中通常區分兩種執行緒:foreground 與 background。有趣的是,"background" 並不是字面上一直在「背景工作」的意思。接下來解釋。
Foreground 執行緒(主要執行緒)
- 預設情況下你建立的執行緒都是 foreground。
- 只要程序中還有至少一個 foreground 執行緒活著,應用程式就不會結束,即使其他都已經「死掉」。
- 例子:程式的主執行緒(Main),以及透過 new Thread() 建立而且沒改動的執行緒。
Background 執行緒(次要執行緒)
- 如果所有的 foreground 執行緒都結束,只剩下 background 執行緒,程序會直接結束,這些 background 執行緒會被無預警中止。
- 用於次要任務:寫日誌、背景發送統計等。
- 要把執行緒設為 background:把屬性 IsBackground 設為 true:
Thread t = new Thread(SomeMethod);
t.IsBackground = true;
t.Start();
差異示範
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread backgroundThread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Background thread 工作中... " + i);
Thread.Sleep(500);
}
Console.WriteLine("Background 執行緒 已完成。");
});
backgroundThread.IsBackground = true; // 把執行緒設為 background!
backgroundThread.Start();
Console.WriteLine("Main 將在 1 秒後結束工作。");
Thread.Sleep(1000); // 等一秒
Console.WriteLine("Main 已結束。Background 執行緒會怎麼樣?");
}
}
你會看到什麼?
Main 可能會先結束,而 background 執行緒會在中途被強制停止。這對那些不應阻止應用關閉的任務很方便。
5. 執行緒的種類
ThreadPool(執行緒池)
- ThreadPool 是個管理執行緒池的機制,避免建立太多執行緒。
- 用在任務多且每個任務很短的場景:並行處理請求、非同步操作等。
- 來自執行緒池的執行緒通常是 background——它們不會阻止應用程式結束。
範例:在執行緒池裡執行程式碼
using System;
using System.Threading;
class Program
{
static void Main()
{
ThreadPool.QueueUserWorkItem(DoWorkInThreadPool, "背景任務");
Console.WriteLine("Main 正在結束工作。");
Thread.Sleep(500);
}
static void DoWorkInThreadPool(object? state)
{
Console.WriteLine("來自執行緒池的執行緒: " + state);
Thread.Sleep(1000); // 嘗試睡眠
Console.WriteLine("來自執行緒池的執行緒 已完成!");
}
}
要注意的:
如果 Main 比執行緒池的工作先結束,執行緒池的執行緒可能會被中斷。如果需要保證完成,請使用 foreground 執行緒或明確等待。
多執行緒的演進:Task、async/await
在現代應用中很少手動建立 Thread。通常會使用 Task 和非同步方法。重點是:
- 來自執行緒池的執行緒與 Task 通常運行在 background 執行緒上。
- 對於大多數情況不需要改變優先順序——遵循常識與最佳實務即可。
6. 有用的細節
執行緒的屬性與行為
| 屬性 | Foreground Thread | Background Thread | ThreadPool Thread |
|---|---|---|---|
|
false 預設 | true | true |
| 應用程式結束 | 只要還有一個 foreground 執行緒活著,應用程式不會結束 | 如果所有 foreground 執行緒結束,應用程式會結束 | 應用程式會在 Main 結束後結束 |
| 控制 | 完全控制 | 完全控制 | 沒有直接控制 |
| 適用場景 | 長期、重要的任務(例如資料庫伺服器) | 非關鍵任務、背景操作、日誌記錄 | 短小任務、Task、非同步操作 |
| 優先順序 | 可以設定 | 可以設定 | 不建議更改 |
不那麼明顯的小技巧與備註
- 只能對普通的執行緒(Thread)改變優先順序,不能對執行緒池裡的任務(Task, ThreadPool)改變。
- 執行緒池裡的執行緒通常具有 Normal 優先順序(不可更改)。
- Task 與 async/await 是更現代的做法,優先順序與背景執行的細節通常被「包在後面」,不需直接管理。
7. 關於優先順序與執行緒類型的常見錯誤
錯誤 #1:濫用優先順序。
把所有執行緒都設定為 Highest 或全部改為 Lowest 而沒有合理原因,通常沒好處。這會破壞任務執行的平衡,降低應用的回應能力。
錯誤 #2:忽略 background 執行緒會被非預期終止。
如果重要工作(例如儲存資料)在 background 執行緒上執行而你沒有等待它完成,可能會導致資料遺失。background 執行緒在程序結束時會被強制中止。
錯誤 #3:對執行緒優先順序抱有過高期望。
執行緒的 Priority 只是給作業系統的建議,不保證該執行緒會先被執行。
GO TO FULL VERSION