在多线程环境下实现单例模式,核心问题是如何保证在并发访问时只有一个实例被创建,并且不会出现竞态条件。下面介绍几种常用且成熟的实现方式,帮助你在实际项目中快速部署线程安全的单例。
1. C++11 之后的本地静态变量(Meyers 单例)
class Singleton {
public:
static Singleton& instance() {
static Singleton instance; // C++11 起保证线程安全
return instance;
}
// 禁止复制构造和赋值操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
~Singleton() = default;
};
- 优点:代码简洁,自动销毁,适用于所有 C++11 及以后编译器。
- 缺点:实例化延迟到第一次调用,无法提前初始化。
2. 双重检查锁(Double-Check Locking)
class Singleton {
public:
static Singleton* instance() {
if (instance_ == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mutex_);
if (instance_ == nullptr) { // 第二次检查
instance_ = new Singleton();
}
}
return instance_;
}
~Singleton() {
delete instance_;
}
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance_;
static std::mutex mutex_;
};
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;
- 优点:延迟实例化,避免不必要的锁开销。
- 缺点:实现复杂,需要保证内存可见性(C++11
std::atomic或volatile)。如果实现不当,可能导致线程安全问题。
3. 枚举单例(Java 风格,C++ 适用)
C++ 11 的枚举类型可用于实现单例,利用枚举的内部静态存储特性:
class Singleton {
public:
static Singleton& get() {
enum Helper { dummy = Singleton::instance() };
return instance();
}
// ...
private:
static Singleton& instance() {
static Singleton instance;
return instance;
}
};
- 优点:确保实例在第一次使用时创建且唯一。
- 缺点:较少人使用,代码可读性略差。
4. std::call_once 与 std::once_flag
class Singleton {
public:
static Singleton& instance() {
std::call_once(flag_, []() { instance_ = new Singleton(); });
return *instance_;
}
~Singleton() { delete instance_; }
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance_;
static std::once_flag flag_;
};
Singleton* Singleton::instance_ = nullptr;
std::once_flag Singleton::flag_;
- 优点:利用标准库提供的一次性初始化机制,简单且安全。
- 缺点:需要手动删除实例(可在
atexit注册析构)。
5. 线程安全的懒加载与销毁
在某些场景下,你可能需要在程序结束前显式销毁单例,以释放资源。可以结合 std::atexit:
class Singleton {
public:
static Singleton& instance() {
static Singleton instance;
std::atexit(&destroy);
return instance;
}
private:
static void destroy() {
// 释放资源
}
};
6. 小结
- 推荐:如果你使用的是 C++11 或更高版本,最简洁可靠的方式是
Meyers Singleton(局部静态变量)。 - 需要手动销毁:如果你想在程序结束前释放资源,考虑
std::call_once或atexit。 - 跨平台兼容:上述实现都基于 C++ 标准库,几乎在所有主流编译器(GCC, Clang, MSVC)上都能安全工作。
通过上述方法,你可以根据具体项目需求,选择最合适的单例实现,既保证线程安全,又保持代码简洁。祝你编码愉快!