← SEARCHABLE ARCHIVE
C / C++ · 2026-08-14

C++ 從基礎到精通 — 第 20 講

以執行緒、atomic 與記憶體模型處理並行

INTERACTIVE READING MODE →

本部落格文章以教學為目的撰寫。 內容可能有誤,請搭配目標編譯器與標準函式庫文件確認。

多個執行緒存取同一記憶體時,問題不只是執行順序不固定。依 C++ memory model,未同步的 data race 是 undefined behavior。

明確保護共享不變式

class Inventory
{
public:
    void Add(Item item)
    {
        std::lock_guard lock(Mutex);
        Items.push_back(std::move(item));
    }

private:
    std::mutex Mutex;
    std::vector<Item> Items;
};

std::lock_guard 在離開 scope 時釋放 mutex,包含例外路徑。鎖應保護完整不變式,而不是只包住一次 vector 呼叫。

以不同順序取得多個 mutex 可能 deadlock。可用 std::scoped_lock 一起鎖定,或制定專案一致的鎖順序。

Atomic 適合小型獨立值

std::atomic<unsigned int> completedJobs{0};

void MarkCompleted()
{
    completedJobs.fetch_add(1, std::memory_order_relaxed);
}

不負責發布其他狀態的統計計數器可能只需要 relaxed ordering。但一個 atomic 計數器不會自動保護多欄位物件的一致性。

預設 sequential consistency 較容易推理。acquire、release、relaxed 等較弱順序應建立在已證明的 happens-before 關係上,而不是效能猜測。

用 jthread 綁定結束生命週期

void Worker(std::stop_token stop)
{
    while (!stop.stop_requested())
        ProcessOneJob();
}

void Run()
{
    std::jthread worker(Worker);
    // 解構時請求停止並 join。
}

C++20 std::jthread 在解構時請求停止並等待執行緒。stop_token 是協作式取消;無法回應的 blocking I/O 仍需 timeout 或喚醒機制。

更好的第一步是移除共享修改狀態。給 worker 獨立輸入,再透過訊息或 queue 收集結果。即使是不同 atomic,若位於同一 cache line 也可能因 false sharing 變慢。

一次測試成功不能證明並行程式安全。應記錄所有權、結束條件、鎖順序與資料發布規則,並以 ThreadSanitizer 和可重現壓力測試驗證。