Big*_*gle 15 c++ if-statement locking
我最近读了一本关于系统软件的书。其中有一个我不理解的例子。
volatile T* pInst = 0;
T* GetInstance()
{
if (pInst == NULL)
{
lock();
if (pInst == NULL)
pInst = new T;
unlock();
}
return pInst;
}
Run Code Online (Sandbox Code Playgroud)
为什么作者要检查(pInst == NULL)两次?
Bod*_*odo 22
当两个线程同时尝试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;
}
Run Code Online (Sandbox Code Playgroud)
另请参阅https://en.wikipedia.org/wiki/Double-checked_locking(摘自interjay的评论)。
注意:此实现要求对它的读写访问volatile T* pInst都是原子的。否则,第二线程可能会读取仅由第一线程写入的部分写入的值。对于现代处理器,尽管并非所有架构都可以保证访问指针值(不是所指向的数据)是一种原子操作。
如果访问pInst不是原子的,则第二个线程可能pInst在获取锁之前进行检查时读取部分写入的非NULL值,然后可能return pInst在第一个线程完成其操作之前执行,这将导致返回错误的指针值。