This is a simple problem, but after reading Why do I need a memory barrier? I'm very confused about it.
In the example below, assume different threads are repeatedly calling Increment and Counter:
class Foo{
int _counter=0;
public int Counter
{
get { return _counter; }
}
public void Increment()
{
Interlocked.Increment(ref _counter);
}
}
Sorry if I'm misinterpreting Why do I need a memory barrier? but it seems like it's suggesting the class above might not be providing a freshness guarantee when reading the value of _counter. Could a thread that's repeatedly accessing the Counter property get forever stuck on an old value of Counter (because it is cached in the register)?
Is a memory barrier or a lock before return _counter;
necessary?
Is a memory barrier or a lock before return _counter; necessary?
Yes, absolutely. Consider the following code.
while (foo.Counter == 0)
{
// Do something
}
The problem is that if the contents inside the loop are simple enough then the C# compiler, JIT compiler, or hardware will optimize the code in this manner.
int register = foo._counter;
while (register == 0)
{
// Do something
}
Or even this.
if (foo._counter == 0)
{
START:
// Do something
goto START;
}
Notice that I use _counter
instead of Counter
as a way of implying that the property will probably be inlined. Then, more importantly, the JIT compiler may "lift" the read of _counter
outside of the loop so that it is only read once.
Memory barriers do not provide a freshness guarantee per se. What they do is prevent certain software or hardware optimizations that reorder reads and writes to memory. The freshness guarantee is more of a side effect really.
So to wrap things up your Counter
property should look like this.
public int Counter
{
get { return Interlocked.CompareExchange(ref _counter, 0, 0); }
}