单例对象在DLL卸载时必须显式销毁,否则会因析构顺序失控导致资源泄漏或崩溃;需在DllMain的DLL_PROCESS_DETACH中调用幂等、线程安全的destroyInstance(),并避免析构依赖。

单例对象在 DLL 或共享库卸载时若未显式销毁,极大概率触发析构函数未调用、资源泄漏、甚至访问已释放内存的崩溃——这不是“可能”,而是 Windows 和 Linux 下都高频复现的问题。
为什么 DLL 卸载时单例不自动销毁
静态变量(包括 static Singleton* instance)的析构顺序由链接器和运行时决定,不受你控制。当 DLL 被 FreeLibrary(Windows)或 dlclose(Linux)卸载时:
- 全局静态对象的析构函数可能已被跳过(尤其跨模块依赖时)
- 若单例内部持有线程、句柄、
std::thread、std::mutex等非 trivial 资源,其析构函数根本不会执行 - 更危险的是:若其他模块的静态析构函数试图访问该单例(比如日志模块先析构,而单例还存着一个 logger 指针),就会读野指针
必须手动注册 DLL 卸载钩子
不能依赖静态对象生命周期,得在 DLL 入口点(DllMain)或初始化函数中主动注册清理逻辑:
- Windows 下,在
DllMain的DLL_PROCESS_DETACH分支中调用Singleton::destroyInstance() - Linux 下,用
__attribute__((destructor))函数(注意:它只对主程序或 dlopen 加载的 SO 有效,且不保证执行顺序) - 更可靠的做法是:提供显式的
shutdown()接口,要求宿主程序在卸载前明确调用
示例(Windows DLL):
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// 可选:提前初始化单例
Singleton::getInstance();
break;
case DLL_PROCESS_DETACH:
// 必须加判空,因为 DLL_PROCESS_DETACH 可能被多次触发
if (lpReserved == nullptr) { // 表示是 FreeLibrary 触发,而非进程退出
Singleton::destroyInstance();
}
break;
}
return TRUE;
}
destroyInstance() 必须是幂等且线程安全的
你写的销毁函数不是“删一次就完事”,它得扛住并发调用、重复调用、以及销毁后被误访问:
- 用
std::atomic标记是否已销毁,避免二次 delete - 销毁前加锁(如果单例本身支持多线程访问),否则
delete和后续getInstance()可能竞态 - 销毁后把指针置为
nullptr,并在getInstance()中检查返回值(或抛异常) - 不要在
destroyInstance()里调用任何可能再次触发单例访问的代码(比如日志、配置读取)
关键片段:
class Singleton {
public:
static void destroyInstance() {
if (destroyed.exchange(true)) return; // 幂等
std::lock_guard<:mutex> lock(mtx);
delete instance;
instance = nullptr;
}
<p>private:
static Singleton* instance;
static std::mutex mtx;
static std::atomic<bool> destroyed;
};</bool></p></:mutex>
比销毁更难的是析构顺序依赖
即便你确保了 destroyInstance() 执行,仍可能出问题:如果单例 A 析构时要访问单例 B,而 B 已经先析构了,就会 crash。这无法靠单个类解决,必须靠模块级约定:
- 所有单例按逆初始化顺序销毁(即谁最后
getInstance(),谁最先destroyInstance()) - 避免单例之间互相持有裸指针;改用弱引用(如
std::weak_ptr)或延迟获取 - 在大型系统中,建议统一用一个“模块管理器”集中注册/销毁所有单例,显式控制顺序
真正棘手的从来不是“怎么删”,而是“删的时候还有谁活着”。这点在跨 DLL 场景下几乎无法靠语言机制兜底,只能靠设计约束和文档强制。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











