类成员函数静态变量多线程访问销毁崩溃问题咨询
问题背景
在模板类classFactory中定义了如下静态成员函数:
template <class T> class classFactory { ... public: ... typedef std::map<KeyType, Creatory> Creator; ... private: ... static Creator &GetCreators() { static Creator creator; return creator; } }
主线程调用exit触发atexit执行全局析构,销毁了该静态成员creator,但此时另有线程仍通过GetCreators()访问该静态变量,导致程序崩溃。
疑问
- 该静态变量的内存分配位置在哪里?我知道未初始化全局变量应在BSS段,但为何它会被全局析构函数销毁,即便它定义在静态成员函数中?
- 有人建议改用
new创建对象来解决问题,即:
static Creator &GetCreators() { static Creator* creator = new Creator; return *creator; }
为何这种方式能解决问题?是因为对象现在分配在堆上吗?为何全局析构函数不会处理它?这种情况下,我该何时销毁该对象?我无法知晓哪个线程会最后执行。
另外,尝试将对象改为thread_local静态变量,但导致程序频繁崩溃(这是一个被其他客户端调用的驱动框架):
static Creators &GetCreators() { thread_local static Creators creators; return creators; }
推测原因是改为thread_local后,对象创建依赖时机,引发了诸多竞争条件。
解答
针对疑问1
- 函数内的静态变量(即
GetCreators()里的creator)存储位置与全局静态变量一致,通常位于数据段(已初始化对象)或BSS段(未初始化对象),具体取决于对象是否有默认初始化操作。 - 无论静态变量定义在全局作用域还是函数内部,C++标准明确规定:程序终止时(如调用
exit),所有拥有静态存储期的对象都会按构造顺序的逆序被析构。函数内的静态变量属于静态存储期范畴,因此会被全局析构流程处理,这和它的定义位置无关。
针对疑问2
- 改用
new创建对象可解决问题的核心原因:new分配的对象位于堆内存,而静态存储期的仅为指针creator(指针本身存于数据/BSS段)。程序结束时只会销毁这个指针变量,不会自动调用delete释放堆上的Creator对象,因此全局析构流程不会触及该对象,也就避免了析构后被其他线程访问的崩溃问题。 - 全局析构函数不处理堆对象的原因是:C++不会自动管理堆内存,
new创建的对象必须显式调用delete才会被销毁,不属于全局静态对象的析构范畴。 - 关于销毁时机:
- 如果驱动框架没有明确的全局退出流程,或无法确定最后一个访问该对象的线程,通常可以选择不主动销毁——程序退出时操作系统会回收整个进程的所有内存(包括堆内存),不会造成内存泄漏;但如果对象持有操作系统级资源(如文件句柄、系统锁等),则需要额外在合适时机释放这些资源。
- 若必须主动销毁,可以在框架提供的全局退出接口中统一调用
delete,或使用std::shared_ptr等线程安全的智能指针来自动管理生命周期,确保多线程环境下引用计数的正确性。
针对thread_local的问题
推测的原因是正确的:thread_local静态变量为每个线程独有,仅在该线程第一次访问时完成构造。在多线程驱动框架中,不同线程的访问时机不确定,可能出现多个线程同时触发对象构造、或某个线程的对象未完成构造就被访问的情况;再加上std::map本身并非线程安全容器,极易引发竞争条件导致崩溃。此外,thread_local对象会在线程退出时析构,若框架存在线程频繁创建销毁的场景,还可能带来额外的析构竞争问题,并不适合这种需要全局共享的工厂场景。
内容的提问来源于stack exchange,提问作者GoswamiK
相关产品推荐
相关产品推荐

