互斥锁指针双重空检查的原因是什么?
What is the reason for double NULL check of pointer for mutex lock
我最近读了一本关于系统软件的书。 里面有一个例子我不明白。
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()
和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
的访问不是原子的,则第二个线程可能会在获取锁之前检查pInst
时读取部分写入的非 NULL 值,然后可能会在第一个线程完成其操作之前执行return pInst
,这将导致返回错误的指针值。
我认为lock()
操作成本很高。我还假设T*
指针的读取是在这个平台上原子完成的,所以你不需要锁定简单的比较pInst == NULL
,因为pInst
值的加载操作将是这个平台上的单个汇编指令。
假设:如果lock()
是一个昂贵的操作,如果我们没有必要,最好不要执行它。所以首先我们检查是否pInst == NULL
.这将是一个单一的汇编指令,所以我们不需要lock()
它。如果pInst == NULL
,我们需要修改它的值,分配新的pInst = new ...
。
但是 - 想象一下一种情况,其中 2 个(或更多)线程正好位于第一个pInst == NULL
和lock()
之前之间的点。两个线程都将pInst = new
.他们已经检查了第一pInst == NULL
,对他们俩来说都是真的。
第一个(任何)线程开始执行并执行lock(); pInst = new T; unlock()
。然后等待lock()
的第二个线程开始执行。当它启动时,pInst != NULL
,因为另一个线程分配了它。所以我们需要再次lock()
内部检查它pInst == NULL
,这样内存就不会泄漏并pInst
被覆盖。
- 使用 gtest 框架在单元测试代码中检查目标对象的私有变量的最佳实践是什么?
- 十进制到二进制的实现不能完全适用于我大学的检查器。问题或提示可能是什么
- 这是什么行检查
- 检查字符串是否与可能的输入之一匹配的最有效方法是什么?
- 互斥锁指针双重空检查的原因是什么?
- 当有多个查询时,检查某些子数组是否排序的有效方法是什么?
- 检查是否存在多维映射的元素的最快方法是什么
- 在转换为较小的数值类型之前执行范围检查的安全、跨平台方法是什么
- 检查我的 C++ 代码!我的cmd屏幕在运行后关闭!我的错误可能是什么
- 使用C或C++检查互联网连接的最快方法是什么
- 检查并集实例之间相等性的正确方法是什么
- 动态检查使用可变继承的类的类型的最佳方法是什么
- 检查模板参数是否为"true"可调用对象的正确方法是什么?
- 检查长类型数据是否在 int 范围内的便携式方法是什么?
- 什么是叮当检查的垃圾值
- 循环边界检查最有效的方法是什么
- 在我的解释器上为脚本代码实现语法检查器的最佳方式是什么
- 检查向量中的所有值是否为真的最优雅的方法是什么<bool>?
- 在c++中,检查给定用户配置文件是否处于活动状态或已删除的最佳方法是什么?
- 使用abi遵从性检查器时出现错误的原因是什么?