在多线程环境下,单例模式需要保证:
- 只创建一次实例;
- 对所有线程可见;
- 线程安全。
下面给出几种常见实现方式,并对比优缺点。
1. 经典懒汉式(双重检查锁定)
#include <mutex>
class Singleton {
public:
static Singleton& getInstance() {
if (!instance_) { // 第一次检查
std::lock_guard<std::mutex> lock(mtx_);
if (!instance_) { // 第二次检查
instance_ = new Singleton();
}
}
return *instance_;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance_;
static std::mutex mtx_;
};
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mtx_;
优点:延迟实例化,性能不错。
缺点:必须保证 new Singleton() 的构造函数不抛异常;在 C++11 之前,存在 static 变量初始化的重排序问题,需使用双重检查锁定。
2. Meyers 单例(函数内部静态局部变量)
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;
};
优点:代码最简洁,C++11 标准保证线程安全。
缺点:实例化时间不确定,若需要在程序启动前创建,需要手动调用。
3. 采用 std::call_once
#include <mutex>
class Singleton {
public:
static Singleton& getInstance() {
std::call_once(initFlag_, []() {
instance_ = new Singleton();
});
return *instance_;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance_;
static std::once_flag initFlag_;
};
Singleton* Singleton::instance_ = nullptr;
std::once_flag Singleton::initFlag_;
优点:显式控制一次性初始化,避免了重复检查。
缺点:代码相对繁琐,仍需手动删除实例(可放在 atexit 或使用 std::unique_ptr)。
4. 结合 std::shared_ptr 与 std::weak_ptr
如果需要在多个地方共享单例,可使用 std::shared_ptr:
class Singleton {
public:
static std::shared_ptr <Singleton> getInstance() {
std::lock_guard<std::mutex> lock(mtx_);
if (auto ptr = instance_.lock()) {
return ptr;
}
auto ptrNew = std::shared_ptr <Singleton>(new Singleton());
instance_ = ptrNew;
return ptrNew;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static std::weak_ptr <Singleton> instance_;
static std::mutex mtx_;
};
std::weak_ptr <Singleton> Singleton::instance_;
std::mutex Singleton::mtx_;
优点:可以让单例被安全地释放,避免内存泄漏。
缺点:略微增加开销,适用于生命周期需要动态管理的场景。
5. 小结
- 最简洁:Meyers 单例(函数内部静态局部变量),推荐在 C++11 及以后使用。
- 延迟初始化、可手动控制:双重检查锁定或
std::call_once。 - 支持共享与自动销毁:
std::shared_ptr/std::weak_ptr方案。
在实际项目中,除非有特殊需求(如跨进程共享或自定义销毁时机),建议直接使用 Meyers 单例;它既简洁又安全。若对实例创建时间有严格控制,可考虑 std::call_once 或手动初始化。