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
相关产品推荐
相关产品推荐

