如何用C++11编写线程安全单例?特定调用代码是否线程安全?
你的代码线程安全性分析
首先看你提供的A::instance()函数:
static A* instance() { static A* ptr = new A(); return ptr; }
在C++11及之后的标准里,静态局部变量的初始化是线程安全的——标准要求编译器自动生成守卫变量(guard variable)相关的同步逻辑,确保new A()只会执行一次,哪怕多个线程同时调用instance()也不会重复初始化。所以auto ptr = A::instance();这行调用本身是线程安全的。
但要注意:这个单例的val_成员是公共的,没有任何同步保护。如果多个线程同时读写val_,会出现数据竞争,这部分是线程不安全的,需要自己加锁(比如用std::mutex)或者把val_改成原子类型(std::atomic<int>)来解决。
C++11推荐的线程安全单例写法
最简洁且符合标准的写法是利用局部静态对象的线程安全初始化特性(Magic Static),不需要手动管理指针和内存,还能避免内存泄漏:
#include <thread> #include <stdio.h> #include <mutex> class A { public: // 禁止拷贝和移动,严格保证单例唯一性 A(const A&) = delete; A& operator=(const A&) = delete; A(A&&) = delete; A& operator=(A&&) = delete; static A& instance() { static A instance; // C++11标准保证这里的初始化是线程安全的 return instance; } // 给val_加读写保护,避免多线程数据竞争 void set_val(int val) { std::lock_guard<std::mutex> lock(mtx_); val_ = val; } int get_val() { std::lock_guard<std::mutex> lock(mtx_); return val_; } private: A() : val_(0) {} // 私有构造函数,确保只能通过instance()创建实例 int val_; std::mutex mtx_; // 保护val_的互斥锁 }; int main() { auto& instance = A::instance(); printf("value: %d\n", instance.get_val()); return 0; }
这种写法的优势:
- 自动线程安全初始化,无需手动加锁
- 程序结束时会自动调用单例的析构函数,避免内存泄漏
- 通过删除拷贝/移动构造函数,从语法上杜绝单例被复制的可能
如果一定要用指针形式(比如需要动态控制生命周期),可以结合std::atomic实现双重检查锁定(C++11及以后支持):
#include <atomic> #include <mutex> class A { public: static A* instance() { A* p = ptr_.load(std::memory_order_acquire); if (!p) { std::lock_guard<std::mutex> lock(mtx_); p = ptr_.load(std::memory_order_relaxed); if (!p) { p = new A(); ptr_.store(p, std::memory_order_release); } } return p; } private: A() = default; static std::atomic<A*> ptr_; static std::mutex mtx_; }; std::atomic<A*> A::ptr_{nullptr}; std::mutex A::mtx_;
不过这种写法比局部静态对象复杂,还需要额外考虑内存释放的问题,一般不推荐,除非有特殊需求。
内容的提问来源于stack exchange,提问作者roderick
相关产品推荐
相关产品推荐

