c++ - 互斥锁指针双重NULL检查的原因是什么
问题描述
我最近读了一本关于系统软件的书。里面有一个例子我不明白。
volatile T* pInst = 0;
T* GetInstance()
{
if (pInst == NULL)
{
lock();
if (pInst == NULL)
pInst = new T;
unlock();
}
return pInst;
}
为什么作者要检查(pInst == NULL)
两次?
解决方案
当两个线程GetInstance()
同时第一次尝试调用时,两者都会pInst == NULL
在第一次检查时看到。一个线程将首先获得锁,这允许它修改pInst
.
第二个线程将等待锁可用。当第一个线程释放锁时,第二个会得到它,现在 的值pInst
已经被第一个线程修改了,所以第二个不需要创建新实例。
只有lock()
和之间的第二次检查unlock()
是安全的。它可以在没有第一次检查的情况下工作,但它会更慢,因为每次调用GetInstance()
都会调用lock()
and unlock()
。第一次检查避免了不必要的lock()
调用。
volatile T* pInst = 0;
T* GetInstance()
{
if (pInst == NULL) // unsafe check to avoid unnecessary and maybe slow lock()
{
lock(); // after this, only one thread can access pInst
if (pInst == NULL) // check again because other thread may have modified it between first check and returning from lock()
pInst = new T;
unlock();
}
return pInst;
}
另请参阅https://en.wikipedia.org/wiki/Double-checked_locking(复制自interjay的评论)。
注意:这个实现要求读写访问volatile T* pInst
都是原子的。否则,第二个线程可能会读取刚刚被第一个线程写入的部分写入值。对于现代处理器,访问指针值(而不是指向的数据)是原子操作,尽管不能保证所有体系结构。
如果访问pInst
不是原子的,则第二个线程可能会在获取锁之前进行检查时读取部分写入的非 NULL 值pInst
,然后可能会return pInst
在第一个线程完成其操作之前执行,这将导致返回错误的指针值。
推荐阅读
- omnet++ - OMNET++:无法将 openflow 扩展导入静脉
- c# - WebAPI 返回“堆栈不足,无法继续安全地执行程序。”
- python - 当我构建一个简单的模型时,在 keras 中引发了 InvalidArgumentError
- c# - 在文件返回类型的 MVC 操作中,文件下载在大约 1 分钟后停止
- asp.net-mvc - 如何在 MVC Razor 语法中设置默认图像
- php - 无法更改 WordPress 管理员邮件
- php - 测试文件下载,但禁止在命令行上输出文件内容
- c - 在附加字符串字符时添加随机值的字符指针
- hibernate-mapping - 应该使用什么类型的休眠映射
- encryption - 组策略在 Windows 中设置强密钥保护时,对 UI 提示有何影响?