1. 多线程单例模式的核心挑战
在C++多线程环境下实现单例模式,最核心的问题在于如何确保线程安全。传统教科书式的单例实现方式在单线程环境下运行良好,但在多线程场景中就可能出现严重的竞态条件问题。
我曾在实际项目中遇到过这样的案例:一个日志管理系统采用懒汉式单例,在高并发场景下偶尔会出现日志丢失的情况。经过调试发现,当多个线程同时调用getInstance()时,可能会创建多个实例,导致日志写入不一致。
1.1 经典单例模式的线程安全问题
最常见的双重检查锁定(DCLP)实现看似完美,但在C++中却存在隐藏的风险:
cpp复制class Singleton {
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mutex);
if (instance == nullptr) { // 第二次检查
instance = new Singleton();
}
}
return instance;
}
private:
static Singleton* instance;
static std::mutex mutex;
};
这段代码的问题在于:instance = new Singleton()实际上包含三个步骤:
- 分配内存
- 构造对象
- 将指针赋值给instance
由于编译器优化可能导致指令重排,可能出现步骤3先于步骤2执行的情况。此时如果另一个线程在第一次检查时发现instance不为空,就会直接使用尚未完全构造的对象。
1.2 C++11后的解决方案
C++11标准引入了内存模型,为我们提供了更优雅的解决方案:
cpp复制class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
