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

C++11 雙重檢查鎖定模式(DCLP)正确实现

     編輯:C++ 入門

雙重檢查鎖定模式(Double-Checked Locking Pattern, DCLP)是單例模式在多線程環境下的經典優化。C++11 前無標準可移植實現,C++11 引入原子操作與內存模型後終得正解。本文詳細解析 DCLP 原理、C++11 實現與 Mintomic 移植方案。

什麼是雙重檢查鎖定?

單例模式最簡單的線程安全寫法是全程加鎖,但實例創建後後續訪問無需鎖。DCLP 思想:先無鎖檢查,若為空再加鎖,鎖內再次檢查並創建。

// 有缺陷的伪代码
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {          // 第一次檢查(無鎖)
        Lock lock;
        if (m_instance == nullptr) {      // 第二次檢查(有鎖)
            m_instance = new Singleton;   // 危險:指針賦值可能早于構造函數完成
        }
    }
    return m_instance;
}

核心問題:內存重排序

編譯器/CPU 可能將 m_instance = new Singleton 重排為:

  1. 分配記憶體
  2. 將指針賦值給 m_instance
  3. 調用構造函數初始化對象

若線程 A 執行到步驟 2,線程 B 進入第一次檢查看到非空指針,直接返回,卻訪問到未完全構造的對象 → 未定義行為。

C++11 正确实现:獲取-釋放語義

使用 std::atomic 配合 memory_order_acquire/release 建立同步關係:

#include <atomic>
#include <mutex>

class Singleton {
    static std::atomic<Singleton*> m_instance;
    static std::mutex m_mutex;
public:
    static Singleton* getInstance() {
        Singleton* tmp = m_instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire); // 獲取屏障
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(m_mutex);
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton;
                std::atomic_thread_fence(std::memory_order_release); // 釋放屏障
                m_instance.store(tmp, std::memory_order_relaxed);
            }
        }
        return tmp;
    }
};
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

關鍵點:

  • acquire 屏障確保後續讀取不被重排到它之前。
  • release 屏障確保前寫入(構造函數)不被重排到它之後。
  • 兩者配對形成 synchronizes-with 關係,保證構造完成對其他線程可見。

Mintomic:為舊編譯器提供 C++11 原子子集

Mintomic 是輕量級 C 庫,在不支持 C++11 的舊編譯器(GCC 4.6、VS2010 等)上實現獲取/釋放屏障與原子指針操作,API 與 C++11 風格一致,代碼幾乎無需修改即可移植。

// Mintomic 版本
#include <mintomic/mintomic.h>

mint_atomicPtr_t Singleton::m_instance = {0};
mint_mutex_t Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = (Singleton*)mint_load_ptr_relaxed(&m_instance);
    mint_thread_fence_acquire();
    if (tmp == NULL) {
        mint_mutex_lock(&m_mutex);
        tmp = (Singleton*)mint_load_ptr_relaxed(&m_instance);
        if (tmp == NULL) {
            tmp = new Singleton;
            mint_thread_fence_release();
            mint_store_ptr_relaxed(&m_instance, tmp);
        }
        mint_mutex_unlock(&m_mutex);
    }
    return tmp;
}

總結

C++11 通過標準化內存模型徹底解決了 DCLP 移植難題。理解獲取-釋放語義是編寫正確無鎖代碼的基石。對於舊工具鏈,Mintomic 提供了實用的過渡方案。

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