执行并发互锁和读取需要内存屏障或锁定
本文关键字:内存 锁定 并发 读取 执行 | 更新日期: 2023-09-27 18:28:57
这是一个简单的问题,但在阅读了《为什么我需要内存屏障?我对此很困惑。
在下面的示例中,假设不同的线程重复调用Increment和Counter:
class Foo{
int _counter=0;
public int Counter
{
get { return _counter; }
}
public void Increment()
{
Interlocked.Increment(ref _counter);
}
}
对不起,如果我误解了为什么我需要记忆障碍?但这似乎表明,在读取counter的值时,上面的类可能没有提供新鲜度保证。重复访问Counter属性的线程会永远停留在旧的Counter值上吗(因为它被缓存在寄存器中)?
return _counter;
之前是否需要内存屏障或锁?
是return _counter之前的内存屏障或锁;必需的
是的,当然。请考虑以下代码。
while (foo.Counter == 0)
{
// Do something
}
问题是,如果循环中的内容足够简单,那么C#编译器、JIT编译器或硬件将以这种方式优化代码。
int register = foo._counter;
while (register == 0)
{
// Do something
}
甚至这个。
if (foo._counter == 0)
{
START:
// Do something
goto START;
}
请注意,我使用_counter
而不是Counter
来暗示该属性可能是内联的。然后,更重要的是,JIT编译器可以将_counter
的读取"提升"到循环之外,使其只读取一次。
内存屏障本身并不能保证的新鲜度。它们所做的是阻止某些软件或硬件优化对内存的读写进行重新排序。新鲜度保证实际上是一种副作用。
因此,为了结束这些内容,Counter
属性应该是这样的。
public int Counter
{
get { return Interlocked.CompareExchange(ref _counter, 0, 0); }
}