初始化可能失败的单例模式(C++)总结
一、核心需求
在多线程环境下实现线程安全的单例模式,需满足以下关键要求:
- 单例对象仅初始化一次
- 支持处理初始化失败场景(返回
nullptr)
- 多线程安全(避免竞态条件)
- 基础约束:私有构造 / 析构函数、禁用拷贝 / 移动操作、全局唯一访问点(
GetInstance())
二、两种实现方案
(一)双重检测锁实现(Singleton_V1)
1. 核心代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #pragma once #include <atomic> #include <mutex> #include <stdexcept> #include <iostream>
class Singleton_V1 { public: static Singleton_V1* GetInstance() { Singleton_V1* inst = instance_.load(std::memory_order_acquire); if (inst == nullptr) { std::lock_guard<std::mutex> lock(mutex_); inst = instance_.load(std::memory_order_relaxed); if (inst == nullptr) { try { inst = new Singleton_V1(); instance_.store(inst, std::memory_order_release); std::atexit(&Destroy); } catch(const std::exception& e) { return nullptr; } } } return inst; }
void dosomething() { std::cout << "Singleton_V1 doing something." << std::endl; }
private: static inline std::atomic<Singleton_V1*> instance_ = {nullptr}; static inline std::mutex mutex_;
bool Init() { return true; }
Singleton_V1() { if (!Init()) { throw std::runtime_error("初始化失败"); } }
static void Destroy() { Singleton_V1 *inst = instance_.exchange(nullptr); delete inst; }
~Singleton_V1() { std::cout << "Singleton_V1 destructed." << std::endl; }
Singleton_V1(const Singleton_V1 &) = delete; Singleton_V1& operator=(const Singleton_V1&) = delete; Singleton_V1(Singleton_V1&&) = delete; Singleton_V1& operator=(Singleton_V1&&) = delete; };
|
2. 设计要点
- 双重检查锁机制:先通过原子指针快速检查,再通过互斥锁保证初始化唯一,兼顾性能与线程安全
- 原子操作:使用
std::atomic修饰实例指针,配合内存序(acquire/release)避免指令重排问题
- 异常安全:
- 内存分配失败直接抛出异常,
catch后返回nullptr
- 初始化失败时,构造函数抛出异常,内存自动释放,
instance_未被赋值,下次可重试
- 资源释放:通过
std::atexit注册销毁函数,程序退出时自动释放单例对象
(二)静态局部变量实现(Singleton_V2)
1. 核心代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #pragma once #include <stdexcept> #include <iostream>
class Singleton_V2 { public: static Singleton_V2* GetInstance() { try { static Singleton_V2 instance; return &instance; } catch (const std::exception& e) { return nullptr; } }
void dosomething() { std::cout << "Singleton_V2 doing something." << std::endl; }
private: bool Init() { return true; }
Singleton_V2() { if (!Init()) { throw std::runtime_error("初始化失败"); } }
~Singleton_V2() { std::cout << "Singleton_V2 destructed." << std::endl; }
Singleton_V2(const Singleton_V2 &) = delete; Singleton_V2& operator=(const Singleton_V2&) = delete; Singleton_V2(Singleton_V2&&) = delete; Singleton_V2& operator=(Singleton_V2&&) = delete; };
|
2. 设计要点
- 线程安全:依赖 C++11 特性,静态局部变量的初始化是线程安全的(编译器保证)
- 自动资源管理:利用 RAII 机制,程序退出时自动调用析构函数释放资源,无需手动管理
- 异常处理:初始化失败时构造函数抛出异常,
GetInstance()捕获后返回nullptr,下次调用仍会尝试初始化
- 简洁高效:代码量少,无需手动维护互斥锁和原子变量,编译器优化充分
三、两种实现对比
| 特性 |
双重检测锁实现(Singleton_V1) |
静态局部变量实现(Singleton_V2) |
| 线程安全保障 |
原子操作 + 互斥锁手动实现 |
C++11 标准编译器自动保障 |
| 资源释放 |
std::atexit注册销毁函数 |
编译器自动调用析构函数 |
| 代码复杂度 |
较高(需处理原子 / 锁 / 内存序) |
极低(依赖语言特性) |
| 初始化重试支持 |
支持(失败后instance_为nullptr) |
支持(失败后下次仍会尝试创建) |
| 兼容性 |
支持 C++11 及以上(需 C++17 支持static inline) |
仅支持 C++11 及以上 |
| 性能 |
首次访问需加锁,后续无开销 |
编译器优化后性能接近,无锁开销 |