You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++经典Singleton线程安全实现复现及运行报错问题咨询

1 完整的线程安全单例实现

按照文章的RAII锁思路,无省略的完整实现代码如下:

#include <iostream>
#include <mutex>
#include <thread>

class Singleton {
public:
    // 禁止外部拷贝、移动对象
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    static Singleton* instance() {
        // RAII锁,离开当前作用域自动释放,无需手动管理解锁
        std::lock_guard<std::mutex> lock(mu2);
        if (pInstance == nullptr) {
            pInstance = new Singleton;
        }
        return pInstance;
    }

    void disp() { 
        std::cout << "disp() called"<< std::endl; 
    }

private:
    // 构造函数私有,禁止外部创建实例
    Singleton() = default;
    // 析构函数私有,禁止外部销毁实例
    ~Singleton() = default;

    static std::mutex mu2;
    static Singleton* pInstance;
};

// 静态成员初始化
std::mutex Singleton::mu2;
Singleton* Singleton::pInstance = nullptr;

void f1() {
    Singleton *p = Singleton::instance();
    p->disp();
}

int main()
{
    std::thread t1(f1);
    t1.join();
    getchar();
    return 0;
}

文章示例中省略的Lock类就是RAII锁封装,对应C++标准库的std::lock_guard,会在函数退出时自动解锁,不需要手动调用unlock。

2 示例程序运行错误的原因

你的代码报错的核心原因是锁的加解锁逻辑不匹配:

  • 你在instance()函数中调用mu2.lock()加锁后,只有Singleton对象析构时才会执行解锁,但正常访问单例的流程不会触发析构,锁会一直处于被持有状态
  • 程序退出时,静态成员mu2被系统销毁,VS运行时检测到mutex还处于加锁状态,就会抛出"mutex destroyed while busy"的错误
  • 额外的逻辑缺陷:你没有将Singleton的构造函数声明为私有,不符合单例模式的基本要求,外部可以随意创建实例。

3 不能在Singleton析构函数中执行mutex unlock操作

绝对不可以将mutex的unlock操作放在Singleton析构中,原因如下:

  • std::mutex要求必须由持有锁的线程执行unlock操作,而Singleton析构的执行线程是不确定的,若和加锁线程不是同一个,会触发未定义行为
  • 锁的加解锁必须一一对应,加锁是在instance函数执行时触发,解锁绑定到无关的析构逻辑上,会导致加锁后长期无法解锁,就是你遇到的报错场景
  • 静态mutex和静态单例指针的销毁顺序在C++中没有明确规定,若析构执行时mutex已经被销毁,调用unlock会直接触发崩溃。

内容的提问来源于stack exchange,提问作者Jatin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:30:00