如何在 C++ 中实现一个线程安全的单例模式?

单例模式(Singleton)是指在整个程序运行期间,只允许存在一个实例,并且可以被全局访问。实现线程安全的单例模式在多线程环境下尤为重要,下面介绍几种常见的实现方式。

1. 局部静态变量(C++11 及以上)

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance; // C++11 起保证线程安全
        return instance;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
private:
    Singleton() = default;
    ~Singleton() = default;
};
  • 优点:代码简洁,使用 C++11 的线程安全初始化特性,零成本。
  • 缺点:如果需要延迟销毁(如程序结束前不销毁),则无法控制。

2. 双重检查锁(Meyers 单例改进版)

class Singleton {
public:
    static Singleton* getInstance() {
        if (!instance) {
            std::lock_guard<std::mutex> lock(mutex);
            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 mutex;
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;
  • 优点:在高并发读场景下性能较好。
  • 缺点:实现复杂,容易出错;C++11 的 static 初始化已足够安全。

3. std::call_oncestd::once_flag

class Singleton {
public:
    static Singleton& getInstance() {
        std::call_once(flag, [](){ instance.reset(new Singleton()); });
        return *instance;
    }
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static std::unique_ptr <Singleton> instance;
    static std::once_flag flag;
};

std::unique_ptr <Singleton> Singleton::instance;
std::once_flag Singleton::flag;
  • 优点:显式控制初始化时机,适合在复杂初始化过程中使用。
  • 缺点:相对冗长,但同样安全。

4. 静态成员指针 + 析构函数(延迟销毁)

如果你需要在程序结束时手动销毁单例,可以使用静态指针和自定义析构函数:

class Singleton {
public:
    static Singleton* getInstance() {
        if (!instance) {
            instance = new Singleton();
            std::atexit(destroyInstance);
        }
        return instance;
    }
private:
    Singleton() = default;
    ~Singleton() = default;
    static void destroyInstance() { delete instance; instance = nullptr; }
    static Singleton* instance;
};

Singleton* Singleton::instance = nullptr;

5. 对比总结

方法 线程安全性 初始化时机 代码简洁度 适用场景
局部静态变量 C++11 以上保证 程序第一次调用 最高 适合大多数情况
双重检查锁 需要手动实现 程序第一次调用 中等 对性能有极致要求
call_once C++11 提供 程序第一次调用 中等 需要手动控制初始化
静态指针 + atexit 需要手动锁 程序第一次调用 中等 需要手动销毁

小结

  • 对于绝大多数 C++ 项目,局部静态变量已足够安全且简洁。
  • 当你需要更细粒度的控制(如延迟销毁或复杂初始化),可以考虑 std::call_onceatexit 方案。
  • 记得总是禁止拷贝构造和赋值操作,确保单例唯一性。

发表评论