Question

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?

Was it helpful?

Solution

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); }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top