C++中线程安全单例模式的正确实现方式
为什么说DCLP不是线程安全的
DCLP(Double Checked Locking Pattern),即双检锁模式:
class Foo {
public:
static Foo* getInstance() noexcept {
if (nullptr == s_pInstance) {
std::lock_guard<std::mutex> lock(s_mutex);
if (nullptr == s_pInstance) {
s_pInstance = new Foo();
}
}
return s_pInstance;
}
private:
static Foo* volatile s_pInstance;
static std::mutex s_mutex;
};
在C++中,volatile关键字只保证数据的可见性,并不保证数据的一致性。所以当外面的判断s_pInstance
非空的时候,由于可能的指令重排,这时对象可能还没有真正的构造好,使得程序无法按照预定的行为执行。
新标准中更好的解决方案
在C++中,静态局部变量的初始化发生在程序第一次执行到变量声明的地方:
static Foo* getInstance() {
static Foo s_instance;
return &s_instance;
}
在C++98中,并没有考虑线程安全的问题,只是简单地用一个条件判断来实现该语义:
static bool s_initialized = false;
static char s_buf[sizeof(Foo)];
static Foo* instance()
{
if (!s_initialized) {
s_initialized = true;
new (s_buf) Foo();
}
return (reinterpret_cast<Foo*>(s_buf));
}
C++11中定义了线程安全的行为,如下是标准草案的6.7节内容:
such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
- 若初始化抛出异常将继续保持未初始化状态
- 若正在初始化,其它运行初始化语句线程将被阻塞
- 若正在初始化的线程递归调用初始化语句行为未定义
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)