马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
留意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。
确保全局唯一实例的线程安全实现
1. 模式定义与用途
核心目标:保证一个类仅有一个实例,并提供全局访问点。
常见场景:
- 日志体系(避免多个日志实例竞争文件资源)
- 设置管理(统一读取和修改全局设置)
- 硬件接口访问(如打印机装备控制)
2. 线程安全的现代 C++ 实现
2.1 方法一:局部静态变量(Meyers’ Singleton,C++11起线程安全)
- #include <iostream>
- class Singleton {
- public:
- // 删除拷贝构造函数和赋值操作符
- Singleton(const Singleton&) = delete;
- Singleton& operator=(const Singleton&) = delete;
- // 获取唯一实例
- static Singleton& getInstance() {
- static Singleton instance; // 线程安全(C++11起)
- return instance;
- }
- void logMessage(const std::string& message) {
- std::cout << "Log: " << message << std::endl;
- }
- private:
- // 私有构造函数,禁止外部创建实例
- Singleton() = default;
- };
- //测试代码
- int main() {
- Singleton::getInstance().logMessage("System started.");
- Singleton::getInstance().logMessage("User logged in.");
- // 验证实例地址唯一
- Singleton& s1 = Singleton::getInstance();
- Singleton& s2 = Singleton::getInstance();
- std::cout << "Addresses equal? " << (&s1 == &s2 ? "Yes" : "No") << std::endl; // 输出:Yes
- return 0;
- }
复制代码 代码剖析:
- 使用局部静态变量 static Singleton instance,C++11 标准保证其初始化线程安全。
- 删除拷贝构造函数和赋值操纵符,防止意外复制实例。
- 私有构造函数确保外部无法直接创建对象。
2.2 方法二:std::call_once(实用于需要动态初始化的场景)
- #include <iostream>
- #include <mutex>
- #include <memory>
- class Singleton {
- public:
- Singleton(const Singleton&) = delete;
- Singleton& operator=(const Singleton&) = delete;
- static Singleton& getInstance() {
- std::call_once(initFlag, []() {
- instance = std::unique_ptr<Singleton>(new Singleton());
- });
- return *instance;
- }
- void logMessage(const std::string& message) {
- std::cout << "Log: " << message << std::endl;
- }
- private:
- Singleton() = default;
- static std::unique_ptr<Singleton> instance;
- static std::once_flag initFlag;
- };
- // 静态成员初始化
- std::unique_ptr<Singleton> Singleton::instance = nullptr;
- std::once_flag Singleton::initFlag;
- // 测试代码(同上)
复制代码 代码剖析:
- std::call_once 保证初始化代码仅实行一次,即使多线程情况下也安全。
- 使用 std::unique_ptr 管理实例,避免内存泄漏。
3. 传统双检锁(Double-Checked Locking)的问题与改进
旧版 C++(非线程安全):
- // 警告:C++11 前的实现可能存在线程安全问题!
- Singleton* Singleton::getInstance() {
- if (instance == nullptr) { // 第一次检查
- std::lock_guard<std::mutex> lock(mutex);
- if (instance == nullptr) { // 第二次检查
- instance = new Singleton();
- }
- }
- return instance;
- }
复制代码 问题:
- 内存读写顺序问题(指令重排可能导致未初始化完成的对象被访问)。
改进(C++11 起使用原子变量):
- #include <atomic>
- class Singleton {
- static std::atomic<Singleton*> instance;
- static std::mutex mutex;
- 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_relaxed);
- if (tmp == nullptr) {
- tmp = new Singleton();
- instance.store(tmp, std::memory_order_release);
- }
- }
- return tmp;
- }
- };
复制代码 4. 应用场景示例:日志体系
- // 在 Singleton 类中添加日志文件操作
- #include <fstream>
- class Singleton {
- // ...(同上)
- private:
- std::ofstream logFile;
- Singleton() {
- logFile.open("app.log", std::ios::app);
- }
- public:
- void logMessage(const std::string& message) {
- if (logFile.is_open()) {
- logFile << message << std::endl;
- }
- }
- ~Singleton() {
- logFile.close();
- }
- };
复制代码 5. 单例模式的优缺点
长处缺点全局唯一实例,避免资源冲突隐藏依靠关系,低沉代码可测试性耽误初始化(节省内存)长期持有资源可能影响程序退出举动 6. 总结与调试技巧
- 验证线程安全:在 VS2022 中使用多线程调试,观察实例地址是否唯一。
- 查察静态变量生命周期:通过断点检查局部静态变量的初始化机遇。
- 禁用拷贝操纵:务必删除拷贝构造函数和赋值操纵符以防止意外复制
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |