这是一个简单的问题,但在阅读Why do I need a memory barrier?后,我对此感到非常困惑。
在下面的示例中,假设不同的线程重复调用Increment和Counter:
class Foo{
int _counter=0;
public int Counter
{
get { return _counter; }
}
public void Increment()
{
Interlocked.Increment(ref _counter);
}
}
很抱歉,如果我误解了Why do I need a memory barrier?,但似乎这表明上述课程在阅读_counter的值时可能没有提供新鲜度保证。重复访问Counter属性的线程是否会永远停留在旧值Counter上(因为它缓存在寄存器中)?
是否需要return _counter;
之前的内存屏障或锁定?
答案 0 :(得分:7)
在返回_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); }
}