推荐使用C++11局部静态变量(Meyers单例),因其由标准保证线程安全、无需手动同步、简洁高效;其次可选DCLP、std::call_once或饿汉式,依场景而定。

如果您尝试在多线程环境中获取单例对象,但发现多个线程同时构造实例或访问到未完全初始化的对象,则可能是由于单例指针发布与对象构造之间缺乏内存序约束。以下是解决此问题的步骤:
一、使用 std::atomic 配合 memory_order_acquire/release 实现 DCLP
该方法通过原子指针操作和严格的内存序语义,确保构造完成对其他线程可见,防止“半构造对象”被读取。核心在于用 std::memory_order_release 约束构造写入顺序,用 std::memory_order_acquire 约束后续读取顺序。
1、声明静态原子指针:static std::atomic
2、声明静态互斥量:static std::mutex mutex_;
立即学习“C++免费学习笔记(深入)”;
3、在 getInstance() 中执行首次无锁检查:调用 instance.load(std::memory_order_acquire),若返回 nullptr 则进入加锁流程。
4、在临界区内执行二次检查:调用 instance.load(std::memory_order_relaxed),避免 acquire 开销,确认仍为空时再执行 new Singleton()。
5、将新构造的指针通过 instance.store(ptr, std::memory_order_release) 发布,确保构造动作不会被重排到 store 之后。
二、采用 C++11 局部静态变量(Meyers 单例)
该方案依赖编译器对函数内 static 局部变量初始化的线程安全保证,由标准强制要求,无需手动管理锁或原子操作,且现代编译器已优化为无竞争分支跳转,兼具简洁性与高性能。
1、将 getInstance() 定义为返回引用:static Singleton& getInstance();
2、在函数体内声明局部静态对象:static Singleton instance;
3、直接返回该对象引用:return instance;
4、禁用拷贝与赋值:在类定义中添加 Singleton(const Singleton&) = delete; 和 Singleton& operator=(const Singleton&) = delete;
5、确保构造函数与析构函数为私有:private: Singleton() = default; ~Singleton() = default;
三、使用 std::call_once 与 std::once_flag 控制初始化
该方法将初始化逻辑封装在回调中,由标准库保证回调仅执行一次,适用于需延迟初始化且构造逻辑复杂、或需显式控制初始化时机的场景,语义清晰且异常安全。
1、声明静态 std::once_flag:static std::once_flag onceFlag;
2、声明静态 std::unique_ptr
3、在 getInstance() 中调用 std::call_once(onceFlag, [&]() { instance.reset(new Singleton()); });
4、返回解引用结果:return *instance;
5、在类外定义静态成员:std::unique_ptr
四、饿汉式静态成员初始化
该方式在程序启动时即完成单例构造,利用全局静态对象初始化的线程安全性(由链接与加载阶段保证),完全规避运行时同步开销,适合小对象且确定必用的场景。
1、在类内声明私有静态成员:static Singleton instance;
2、提供公有静态访问函数:static Singleton& getInstance() { return instance; }
3、在类外定义并初始化该静态成员:Singleton Singleton::instance;
4、禁用拷贝与赋值:Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;
5、将构造函数设为私有:private: Singleton() = default;


















