实现一个线程安全的单例模式(C++20实现)

在 C++ 中,单例模式是一种常见的设计模式,用于保证一个类只有一个实例,并提供全局访问点。随着多线程程序的普及,单例实现需要考虑线程安全。下面给出一个基于 C++20 的线程安全单例实现示例,并解释关键点。

核心实现

#include <mutex>
#include <memory>
#include <iostream>

class Singleton {
public:
    // 删除拷贝构造和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 公开静态成员函数获取单例实例
    static Singleton& getInstance() {
        // C++11 起的局部静态变量在首次进入函数时线程安全地初始化
        // 该实现利用了此特性,确保在多线程环境下也只创建一次实例
        static Singleton instance;
        return instance;
    }

    // 示例成员函数
    void doSomething() {
        std::lock_guard<std::mutex> lock(mtx_);
        std::cout << "Instance address: " << this << "\n";
    }

private:
    // 私有构造函数
    Singleton() {
        std::cout << "Singleton constructed\n";
    }

    std::mutex mtx_; // 保护内部状态的互斥锁
};

使用示例

#include <thread>
#include <vector>

void worker(int id) {
    Singleton& s = Singleton::getInstance();
    s.doSomething();
}

int main() {
    const int threadCount = 10;
    std::vector<std::thread> threads;

    for (int i = 0; i < threadCount; ++i) {
        threads.emplace_back(worker, i);
    }

    for (auto& t : threads) {
        t.join();
    }

    return 0;
}

运行结果(示例):

Singleton constructed
Instance address: 0x7ffeefbff5a0
Instance address: 0x7ffeefbff5a0
Instance address: 0x7ffeefbff5a0
...

关键点解析

  1. 局部静态变量的线程安全

    • C++11 起,编译器保证局部静态变量的初始化是线程安全的。static Singleton instance; 在第一次被 getInstance() 调用时只会被初始化一次,其他线程会等待初始化完成。
  2. 避免全局初始化顺序问题

    • 传统的全局静态对象可能存在“静态初始化顺序悖论”。通过懒加载(lazy initialization),只在真正需要时才创建实例,避免了这种问题。
  3. 删除拷贝构造和赋值

    • 为了防止复制单例实例,显式删除拷贝构造函数和赋值运算符。
  4. 内部状态的同步

    • doSomething() 中使用 std::mutex 保护实例内部状态,确保多个线程调用该成员函数时不会产生竞争。若成员函数只是读取状态且已保证状态本身不变,可省略锁。
  5. C++17 以上的 std::call_once 替代方案

    • 也可以使用 std::call_oncestd::once_flag 来实现单例初始化,但 static 局部变量的方式更简洁、易读。

性能注意

  • getInstance() 的调用几乎是零成本的:局部静态变量的检查在编译器层面优化为单次初始化后直接返回。
  • doSomething() 的互斥锁会在多线程频繁调用时成为瓶颈,可根据业务需求使用读写锁或无锁设计。

总结

通过利用 C++11 及以后标准中对局部静态变量线程安全初始化的保证,配合删除拷贝构造函数、使用 std::mutex 保护内部状态,便可以实现一个简单、高效且线程安全的单例模式。该实现兼具易读性与可靠性,适合在大多数 C++ 项目中直接使用。

发表评论