C++/CLI 透過 System::Threading 命名空間提供豐富的執行緒同步、管理與通訊機制。本文涵蓋 Monitor 等待/脈衝模式、執行緒狀態管理、volatile 語意、執行緒區域儲存(TLS)、Interlocked 原子操作等核心主題。
Monitor 提供物件層級的鎖與條件變數語意,適合生產者/消費者模式。
using namespace System;
using namespace System::Threading;
public ref class MessageBuffer {
String^ messageText;
public:
void SetMessage(String^ s) {
Monitor::Enter(this);
messageText = s;
Monitor::Pulse(this); // 喚醒一個等待執行緒
Console::WriteLine("Set new message {0}", messageText);
Monitor::Exit(this);
}
void ProcessMessages() {
Monitor::Enter(this);
while (true) {
try {
Monitor::Wait(this); // 釋放鎖並等待,被喚醒後重新取得鎖
} catch (ThreadInterruptedException^) {
Console::WriteLine("ProcessMessage interrupted");
return;
}
Console::WriteLine("Processed new message {0}", messageText);
}
Monitor::Exit(this);
}
};
public ref class CreateMessages {
MessageBuffer^ msg;
public:
CreateMessages(MessageBuffer^ m) : msg(m) {}
void CreateMessagesEntryPoint() {
for (int i = 1; i <= 5; ++i) {
msg->SetMessage(String::Concat("M-", i.ToString()));
Thread::Sleep(2000); // 模擬產生間隔
}
Console::WriteLine("CreateMessages thread terminating");
}
};
public ref class ProcessMessages {
MessageBuffer^ msg;
public:
ProcessMessages(MessageBuffer^ m) : msg(m) {}
void ProcessMessagesEntryPoint() {
msg->ProcessMessages();
Console::WriteLine("ProcessMessages thread terminating");
}
};
int main() {
MessageBuffer^ m = gcnew MessageBuffer;
ProcessMessages^ pm = gcnew ProcessMessages(m);
Thread^ pmt = gcnew Thread(gcnew ThreadStart(pm, &ProcessMessages::ProcessMessagesEntryPoint));
pmt->Start();
CreateMessages^ cm = gcnew CreateMessages(m);
Thread^ cmt = gcnew Thread(gcnew ThreadStart(cm, &CreateMessages::CreateMessagesEntryPoint));
cmt->Start();
cmt->Join(); // 等待生產者結束
pmt->Interrupt(); // 中斷消費者等待
pmt->Join(); // 等待消費者結束
Console::WriteLine("Primary thread terminating");
}
Monitor::Wait:原子性釋放鎖並進入等待隊列,被 Pulse/PulseAll 喚醒後重新競爭鎖Thread::Interrupt:喚醒 Wait/Sleep/Join 狀態的執行緒,拋出 ThreadInterruptedExceptionWait,否則首則訊息的 Pulse 會落空Join() 可指定逾時毫秒數,避免無限等待
Set new message M-1
Processed new message M-1
Set new message M-2
Processed new message M-2
Set new message M-3
Processed new message M-3
Set new message M-4
Processed new message M-4
Set new message M-5
Processed new message M-5
CreateMessages thread terminating
ProcessMessage interrupted
ProcessMessages thread terminating
Primary thread terminating
| 屬性/方法 | 用途 |
|---|---|
IsBackground | 前景/背景執行緒。背景執行緒隨主執行緒結束而終止 |
IsAlive | 檢查執行緒是否仍在執行 |
Thread::Sleep(ms) | 放棄 CPU 切片指定毫秒 |
Thread::CurrentThread | 取得目前執行緒的 Thread 物件 |
Priority | 優先級:Lowest ~ Highest(預設 Normal),不宜過度依賴 |
volatile 告訴編譯器:該變數可能被多執行緒非同步讀寫,禁止激進最佳化。
volatile int i = 0;
i = 10; // 必須產生寫入指令
i = 20; // 不會被最佳化移除第一次寫入
// 錯誤:i 可能在兩次讀取間被其他執行緒修改
if (i < 5 || i > 10) { ... }
// 正確:快照後再判斷
int copy = i;
if (copy < 5 || copy > 10) { ... }
注意:volatile 不提供原子性或記憶體屏障,僅防編譯器最佳化。真正的跨執行緒同步需用 lock/Monitor/Interlocked。
[ThreadStatic]結合區域變數私有性與靜態變數持久性:每個執行緒擁有獨立副本,執行緒建立時配置、結束時釋放。
public ref class ThreadX {
int m1; // 實例欄位:每個 ThreadX 實例一份
static int m2 = 20; // 靜態欄位:全程式共享一份
[ThreadStatic] static int m3 = 30; // TLS:每個執行緒一份
public:
ThreadX() : m1(10) {}
void TMain() {
String^ name = Thread::CurrentThread->Name;
Monitor::Enter(ThreadX::typeid);
for (int i = 1; i <= 5; ++i) { ++m1; ++m2; ++m3; }
Console::WriteLine("Thread {0}: m1={1}, m2={2}, m3={3}", name, m1, m2, m3);
Monitor::Exit(ThreadX::typeid);
}
};
int main() {
Thread::CurrentThread->Name = "t0";
ThreadX^ o1 = gcnew ThreadX;
Thread^ t1 = gcnew Thread(gcnew ThreadStart(o1, &ThreadX::TMain)); t1->Name = "t1";
ThreadX^ o2 = gcnew ThreadX;
Thread^ t2 = gcnew Thread(gcnew ThreadStart(o2, &ThreadX::TMain)); t2->Name = "t2";
t1->Start();
(gcnew ThreadX)->TMain(); // 主執行緒直接呼叫(非新執行緒)
t2->Start();
t1->Join(); t2->Join();
}
Thread t0: m1=15, m2=25, m3=35
Thread t1: m1=15, m2=30, m3=5
Thread t2: m1=15, m2=35, m3=5
m1:每個 ThreadX 實例獨立,初始 10 + 5 = 15m2:全域共享,3 執行緒各 +5 → 20+15=35m3:TLS,t1/t2 經執行緒建立 → 初始化為 0(非 30)+5 = 5;t0 非執行緒建立 → 保留初始值 30 +5 = 35關鍵:TLS 變數在執行緒建立機制下才會初始化,直接呼叫方法不會觸發。
多執行緒對共享整數遞增(++value)非原子:讀取-修改-寫入三步驟可能被打斷。
using namespace System;
using namespace System::Threading;
static bool useInterlocked = false;
const int MAX = 10000000;
static long long value = 0;
void TMain() {
if (useInterlocked) {
for (int i = 1; i <= MAX; ++i) Interlocked::Increment(value);
} else {
for (int i = 1; i <= MAX; ++i) ++value;
}
}
int main(array^ argv) {
if (argv->Length == 1 && (argv[0] == "Y" || argv[0] == "y"))
useInterlocked = true;
Thread^ t1 = gcnew Thread(gcnew ThreadStart(&TMain));
Thread^ t2 = gcnew Thread(gcnew ThreadStart(&TMain));
Thread^ t3 = gcnew Thread(gcnew ThreadStart(&TMain));
t1->Start(); t2->Start(); t3->Start();
t1->Join(); t2->Join(); t3->Join();
Console::WriteLine("After {0} ops, value = {1}", 3 * MAX, value);
}
| 模式 | 執行 5 次結果範例 | 正確值 | 遺失率 |
|---|---|---|---|
++value | 14.3M, 24.5M, 20.0M, 24.2M, 25.4M | 30,000,000 | 17%~50% |
Interlocked::Increment | 30,000,000 (每次) | 30,000,000 | 0% |
Increment(ref int/long):原子遞增,回傳新值Decrement(ref int/long):原子遞減Exchange(ref T, T):原子交換CompareExchange(ref T, T, T):原子比較交換(CAS)Add(ref int/long, int/long):原子加法Read(ref long):原子讀取 64 位元整數| 場景 | 建議機制 | 備註 |
|---|---|---|
| 簡單整數計數器 | Interlocked | 無鎖、效能最佳 |
| 複雜關鍵區段 | Monitor::Enter/Exit 或 lock | 支援條件變數 |
| 生產者/消費者 | Monitor::Wait/Pulse | 經典模式 |
| 讀多寫少共享資料 | ReaderWriterLock | .NET 2.0+ |
| 跨程序域同步 | Mutex, Semaphore | 核心物件、較重 |
| 執行緒專屬狀態 | [ThreadStatic] | 無同步開銷 |
| 防編譯器最佳化 | volatile | 非同步工具,勿濫用 |