多线程调用 getinstance() 崩溃或返回不同地址是因竞态条件导致的运行时异常,根本原因是 if 判断与 new 操作缺乏原子性和内存屏障;c++11 推荐使用局部静态变量实现线程安全单例,若需指针及动态参数则须配合 std::atomic 与正确内存序的双重检查锁定。

报错现象:多个线程同时调用 getInstance() 导致崩溃或返回不同地址
这是典型的竞态条件表现,不是编译错误,而是运行时行为异常。常见现象包括:Segmentation fault、double free、instance 指针值在不同线程中不一致、构造函数被多次执行。根本原因是 if (instance == nullptr) 和 instance = new Singleton() 之间没有原子性保护。
懒汉式单例未加锁的典型错误写法
下面这种写法在多线程下绝对不可用:
Singleton* Singleton::getInstance() {
if (instance == nullptr) { // 检查点1:非原子读
instance = new Singleton(); // 创建点:非原子写+构造
}
return instance;
}
问题在于:
-
instance == nullptr是普通内存读,不带同步语义 -
new Singleton()包含内存分配 + 构造函数调用,若两个线程同时进入,会创建两个对象 - 即使只有一方成功赋值,另一方也可能看到未完全构造的
instance(因缺乏内存屏障)
C++11 及以后推荐的线程安全写法:局部静态变量
最简、最安全、无需手动加锁的方式是利用 C++11 标准对局部静态变量初始化的线程安全保证:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 起,首次调用时线程安全地构造
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
注意点:
- 必须返回引用(
Singleton&),避免拷贝;若需指针,可用&getInstance() - 构造函数不能抛异常,否则后续调用会直接
std::terminate - 析构时机由静态存储期决定,程序退出时自动销毁,无需手动管理
- 不适用于需要延迟初始化但又依赖运行时参数的场景(比如传入配置)
需要动态参数或显式控制生命周期时,用双重检查锁定(DCLP)要加 std::atomic 和内存序
如果必须用指针 + 延迟初始化 + 运行时参数,传统 DCLP 在 C++11 前有重排序问题,现在必须配合 std::atomic:
class Singleton {
public:
static Singleton* getInstance() {
Singleton* tmp = instance.load(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<:mutex> lock(mtx);
tmp = instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton();
instance.store(tmp, std::memory_order_release);
}
}
return tmp;
}
private:
static std::atomic<singleton> instance;
static std::mutex mtx;
};</singleton></:mutex>
关键细节:
-
instance必须是std::atomic<singleton></singleton>,不能是裸指针 - 第一次 load 用
memory_order_acquire,确保后续读能看到构造完成 - store 用
memory_order_release,防止构造动作被重排到 store 之后 - 内部二次检查不能省略——防止多个线程同时拿到锁后重复 new
真正容易被忽略的是内存序和原子变量声明本身:漏掉 std::atomic 或写成 memory_order_seq_cst 以外的宽松序却没配对,都会导致未定义行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











