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

C++Builder 多執行緒程式設計技術深度解析:同步機制與生產者-消費者實作

     編輯:VC 教學

前言

多執行緒能提升回應性、發揮多核效能、隔離阻塞操作。但共享資料存取帶來同步挑戰。本文說明 Windows 執行緒模型、C++Builder TThread 架構、同步物件、經典生產者-消費者模式實作。

一、為何需要多執行緒?

場景單執行緒痛點多執行緒效益
MDI 應用單一視窗阻塞影響全程式每視窗獨立執行緒,互不干擾
多核 CPU僅用單核Windows 排程分派至多核並行
耗時運算/IOUI 凍結、無回應背景執行緒處理,UI 保持流暢
網路伺服器序列處理請求,延遲高每連線/請求獨立執行緒/執行緒池

二、Windows 執行緒模型要點

  • 進程 = 資源容器 (位址空間、控制代碼、安全權杖)
  • 執行緒 = 排程單位 (堆疊、暫存器狀態、執行緒區域儲存 TLS)
  • 同進程執行緒共享:全域變數、堆積、程式碼、開啟控制代碼
  • 時間片輪轉排程,單核同一時刻僅一執行緒執行
  • 不可預測何時被搶佔 → 共享資料需同步

三、C++Builder TThread 架構回顧

  • 建立衍生類別,覆寫 Execute()
  • FreeOnTerminate = true 自動釋放物件
  • UI 操作必須 Synchronize() 或 Queue()
  • 共享資料用 TCriticalSection/TMutex/TSemaphore/TEvent 保護

四、同步物件對比

物件適用場景關鍵方法
TCriticalSection進程內短暫臨界區Enter()/Leave(), Acquire()/Release()
TMutex跨進程同步、命名互斥WaitFor()/ReleaseMutex()
TSemaphore資源計數控制 (連線池、緩衝區)WaitFor()/Release()
TEvent執行緒間通知/等待條件SetEvent()/ResetEvent()/WaitFor()
TInterlocked原子整數操作 (計數器、旗標)Increment/Decrement/Exchange/CompareExchange

五、經典問題:生產者-消費者

場景:緩衝區大小 N。生產者產生資料放入,滿則等待;消費者取出處理,空則等待。

共享資料結構

class BoundedBuffer {
    std::vector buffer;
    size_t capacity;
    size_t head = 0, tail = 0, count = 0;

    TCriticalSection cs;          // 保護緩衝區結構
    TEvent notFull;               // 生產者等待:緩衝區非滿
    TEvent notEmpty;              // 消費者等待:緩衝區非空
    // 手動重置事件,避免喚醒單一執行緒不足
public:
    BoundedBuffer(size_t cap) : capacity(cap), buffer(cap),
        notFull(true, false), notEmpty(true, false) {}

    void Produce(int value) {
        cs.Enter();
        try {
            while (count == capacity) {
                cs.Leave();
                notFull.WaitFor(INFINITE); // 等待非滿
                cs.Enter();
            }
            buffer[tail] = value;
            tail = (tail + 1) % capacity;
            ++count;
            notEmpty.SetEvent(); // 通知消費者有資料
        }
        __finally { cs.Leave(); }
    }

    int Consume() {
        int value;
        cs.Enter();
        try {
            while (count == 0) {
                cs.Leave();
                notEmpty.WaitFor(INFINITE);
                cs.Enter();
            }
            value = buffer[head];
            head = (head + 1) % capacity;
            --count;
            notFull.SetEvent(); // 通知生產者有空位
            return value;
        }
        __finally { cs.Leave(); }
    }
};

生產者執行緒

class ProducerThread : public TThread {
    BoundedBuffer& buffer;
    int id;
protected:
    void __fastcall Execute() {
        for (int i = 0; !Terminated && i < 100; ++i) {
            int data = id * 1000 + i;
            buffer.Produce(data);
            Synchronize([=] { Form1->Log("Producer " + IntToStr(id) + " produced " + data); });
            Sleep(Random(100));
        }
    }
public:
    __fastcall ProducerThread(BoundedBuffer& buf, int id_, bool suspended)
        : TThread(suspended), buffer(buf), id(id_) {}
};

消費者執行緒

class ConsumerThread : public TThread {
    BoundedBuffer& buffer;
    int id;
protected:
    void __fastcall Execute() {
        while (!Terminated) {
            int data = buffer.Consume();
            Synchronize([=] { Form1->Log("Consumer " + IntToStr(id) + " consumed " + data); });
            Sleep(Random(150));
        }
    }
public:
    __fastcall ConsumerThread(BoundedBuffer& buf, int id_, bool suspended)
        : TThread(suspended), buffer(buf), id(id_) {}
};

主程式啟動

BoundedBuffer buffer(10);
ProducerThread* p1 = new ProducerThread(buffer, 1, false);
ProducerThread* p2 = new ProducerThread(buffer, 2, false);
ConsumerThread* c1 = new ConsumerThread(buffer, 1, false);
ConsumerThread* c2 = new ConsumerThread(buffer, 2, false);
// 結束時 Terminate + WaitFor

六、現代 C++ 替代方案 (C++11+)

#include <mutex>
#include <condition_variable>
#include <queue>

class ModernBuffer {
    std::queue q;
    size_t capacity;
    std::mutex mtx;
    std::condition_variable notFull, notEmpty;
public:
    ModernBuffer(size_t cap) : capacity(cap) {}

    void Produce(int v) {
        std::unique_lock lk(mtx);
        notFull.wait(lk, [&]{ return q.size() < capacity; });
        q.push(v);
        notEmpty.notify_one();
    }

    int Consume() {
        std::unique_lock lk(mtx);
        notEmpty.wait(lk, [&]{ return !q.empty(); });
        int v = q.front(); q.pop();
        notFull.notify_one();
        return v;
    }
};
  • std::mutex + std::condition_variable 為標準、可攜、型別安全
  • std::unique_lock 配合 wait(predicate) 自動處理虛假喚醒
  • C++Builder 10.4+ 完整支援,新專案優先採用

七、多執行緒除錯技巧

  • IDE:執行緒視窗、呼叫堆疊、凍結/解凍執行緒
  • 命名執行緒:SetThreadName(-1, "Producer-1"); (需自定義函數)
  • 死結偵測:TMonitor / std::mutex 無內建,需記錄鎖順序、超時機制
  • 資料競爭:啟用 ThreadSanitizer (Clang/GCC) 或 Concurrency Visualizer (VS)
  • 日誌加入執行緒 ID:GetCurrentThreadId()

結語

多執行緒威力巨大,同步正確性是生命線。掌握 TCriticalSection/TEvent 等 VCL 同步物件、經典模式 (生產者-消費者、讀寫鎖、未來/承諾)、現代 C++ 標準庫替代方案,並養成「鎖最小化、避免巢狀鎖、RAII 管理鎖」習慣,才能駕馭並行複雜度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved