首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++/CLI 執行緒程式設計:同步機制、執行緒區域儲存與原子操作

     編輯:關於C++

前言

C++/CLI 透過 System::Threading 命名空間提供豐富的執行緒同步、管理與通訊機制。本文涵蓋 Monitor 等待/脈衝模式、執行緒狀態管理、volatile 語意、執行緒區域儲存(TLS)、Interlocked 原子操作等核心主題。

Monitor 同步:等待/脈衝模式

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 狀態的執行緒,拋出 ThreadInterruptedException
  • 啟動順序重要:消費者必須先進入 Wait,否則首則訊息的 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 = 15
  • m2:全域共享,3 執行緒各 +5 → 20+15=35
  • m3:TLS,t1/t2 經執行緒建立 → 初始化為 0(非 30)+5 = 5;t0 非執行緒建立 → 保留初始值 30 +5 = 35

關鍵:TLS 變數在執行緒建立機制下才會初始化,直接呼叫方法不會觸發。

原子操作

多執行緒對共享整數遞增(++value)非原子:讀取-修改-寫入三步驟可能被打斷。

範例:三執行緒各遞增 1000 萬次


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 次結果範例正確值遺失率
++value14.3M, 24.5M, 20.0M, 24.2M, 25.4M30,000,00017%~50%
Interlocked::Increment30,000,000 (每次)30,000,0000%

Interlocked 核心方法

  • 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非同步工具,勿濫用
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved