3

这是一个简单的问题,但是在阅读了为什么我需要内存屏障?我对此很困惑。

在下面的示例中,假设不同的线程重复调用 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;

4

1 回答 1

8

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); }
}
于 2012-03-23T13:27:34.887 回答