在双重检查的锁定模式中获取屏障 [英] Acquire barrier in the double checked locking pattern

查看:72
本文介绍了在双重检查的锁定模式中获取屏障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++和双重检查锁定的风险中,作者举了一个例子关于如何正确实施该模式.

In C++ and the Perils of Double-Checked Locking, the authors give an example on how to implement the pattern correctly.

Singleton* Singleton::instance () {
   Singleton* tmp = pInstance;
   ... // insert memory barrier (1)
   if (tmp == 0) {
      Lock lock;
      tmp = pInstance;
      if (tmp == 0) {
         tmp = new Singleton;
         ... // insert memory barrier (2)
         pInstance = tmp;
      }
   }
   return tmp;
}

但是,我不知道是第一个内存屏障是否必须在Singleton* tmp = pInstance;之后? (很清楚,我知道需要障碍.我不了解的是分配tmp之后是否必须出现障碍).如果是,为什么呢?以下无效吗?

What I couldn't figure out, though, is if the first memory barrier must be after Singleton* tmp = pInstance;? ( To be clear, I understand that the barrier is needed. What I don't understand is if it must come after assigning tmp) If so why? Is the following not valid?

Singleton* Singleton::instance () {
   ... // insert memory barrier (1)
   if (pInstance == 0) {
      Lock lock;
      if (pInstance == 0) {
         Singleton* tmp = new Singleton;
         ... // insert memory barrier (2)
         pInstance = tmp;
      }
   }
   return pInstance;
}

推荐答案

这是必不可少的.否则,在复制之前,CPU可能会预取if之后发生的读取,这将是灾难.如果pInstance不为NULL并且我们不获取任何锁,则必须保证在代码中读取pInstance之后发生的读取不会在读取pInstance之前重新排序.

It is essential. Otherwise, reads that occur after the if may be prefetched by the CPU before the copy, which would be a disaster. In the case where pInstance is not NULL and we don't acquire any locks, you must guarantee that reads that occur after the read of pInstance in the code are not re-ordered to before the read of pInstance.

考虑:

Singleton* tmp = pInstance;
if (tmp == 0) { ... }
return tmp->foo;

如果CPU在tmp之前读取tmp->foo会发生什么?例如,CPU可以将其优化为:

What happens if the CPU reads tmp->foo before tmp? For example, the CPU could optimize this to:

bool loaded = false;
int return_value = 0;

if (pInstance != NULL)
{ // do the fetch early
     return_value = pInstance->foo;
     loaded = true;
}

Singleton* tmp = pInstance;
if (tmp == 0) { ... }

return loaded ? return_value : tmp->foo;

注意这是做什么的? tmp->foo的读取现在已移至检查指针是否为非NULL之前.这是CPU可能执行的完全合法的内存预取优化(推测读取).但这对双重检查锁定的逻辑绝对是灾难性的.

Notice what this does? The read of tmp->foo has now moved to before the check if the pointer is non-NULL. This is a perfectly legal memory prefetch optimization (speculative read) that a CPU might do. But it's absolutely disastrous to the logic of double checked locking.

在我们将pInstance视为非NULL之前,if (tmp == 0)之后的代码不要预取任何内容绝对至关重要.因此,您需要采取一些措施来防止CPU重新组织上述代码的内存操作.内存屏障可以做到这一点.

It is absolutely vital that code after the if (tmp == 0) not prefetch anything from before we see pInstance as non-NULL. So you need something to prevent the CPU from reorganizing the code's memory operations as above. A memory barrier does this.

这篇关于在双重检查的锁定模式中获取屏障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆