本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《
阿里云开发者社区用户服务协议
》和
《
阿里云开发者社区知识产权保护指引
》。如果您发现本社区中有涉嫌抄袭的内容,填写
侵权投诉表单
进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
static Singleton* getInstance() {
if(nullptr == _pInstance) {
_pInstance = new Singleton();
return _pInstance;
private:
// 构造函数和析构函数私有化
Singleton();
~Singleton();
// 禁掉拷贝构造、拷贝赋值、移动构造、移动赋值
Singleton(const Singleton &) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete;
private:
// 静态成员:静态成员函数只能访问静态成员
static Singleton* _pInstance;
// 静态成员需要初始化
Singleton* Singleton::_instance = nullptr;
// 线程安全,双重检测:double check
if (nullptr == _pInstance) {
std::lock_guard<std::mutex> lock(_mutex);
if (nullptr == _pInstance) {
// 问题:多线程环境下,cpu reorder
_pInstance = new Singleton();
// 注册回调函数,进程结束后,调用销毁函数
atexit(Singleton::Destructor);
return _pInstance;
private:
Singleton();
~Singleton();
Singleton(const Singleton &) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete;
// 注册销毁函数为atexit的回调函数,用于在进程结束后释放堆空间
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
private:
static Singleton* _pInstance;
Singleton* Singleton::_instance = nullptr;
问题:new 操作符指令重排
C++ 98 表达单线程语义。而在多核多线程的情况下,若 cpu 指令重排,例如:对于 new 运算符的指令执行:分配内存、调用构造函数、返回指针。若发生 cpu 指令重排,会优化为分配内存、返回指针,却还没有调用构造函数初始化数据。此时,若有其他线程访问,可能造成程序的崩溃。
实现 3:原子变量 + 懒汉模式
C++ 11:多线程语义,cpu 指令重排,提供同步原语:原子变量、内存屏障等
原子变量解决
原子性问题
可见性问题:load 可以看见其他线程最新操作的数据, store 修改数据让其他线程可见
执行序问题:
memory_order_acuire
不能重排指令,
memory_order_release
松散指令,可以重排指令。
内存屏障(内存栅栏)解决
可见性问题
执行序问题
使用原子变量解决原子性、可见性、执行序
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_acquire);
if (tmp == nullptr) {
tmp = new Singleton;
_instance.store(tmp, memory_order_release);
atexit(Destructor);
return tmp;
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
std::atomic<Singleton*> Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化
改进:若构造函数中存在其他原子性操作,则可以使用松散的指令执行方式,提升运行速度。使用内存屏障,避免 tmp 指针在 new 操作未执行完就返回给用户。
原子变量解决:原子性、可见性
内存栅栏解决:执行序
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
// 获取内存屏障
std::atomic_thread_fence(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
// 释放内存屏障
std::atomic_thread_fence(std::memory_order_release);
_instance.store(tmp, std::memory_order_relaxed);
atexit(Destructor);
return tmp;
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
std::atomic<Singleton*> Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化
static Singleton* Singleton::getInstance() {
if(nullptr == _pInstance) {
_pInstance = new Singleton();
atexit(Singleton::Destructor);
return _pInstance;
// 全局初始化,使其在进程创建之前就不为空,防止子进程创建对象
Singleton* Singleton::_instance = getInstance();
问题:无论是否需要该类实例,都必须提前创建。
* 实现5:magic static
源自:C++ effective,C++ 11 magic static 特性,参考官方文档:
静态局部变量 static / thread local
,推荐使用。
静态局部变量在初始化的时候,并发线程同时进入声明语句,并发线程会阻塞等待其初始化结束。线程安全。
静态局部变量首次经过它的声明才会被初始化,在其后所有的调用中,声明都会被跳过。
因此,使用定义在栈上的局部静态变量保存单例对象,具备所有优点:
系统自动调用析构函数,回收内存
没有 new 操作带来的 cpu reorder 操作
~Singleton();
Singleton(const Singleton &) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete;