What is the difference between Interlocked.Exchange
and Volatile.Write
?
Both methods update value of some variable. Can someone summarize when to use each of them?
In particular I need to update double
item of my array, and I want another thread to see the freshest value. What is preferred? Interlocked.Exchange(ref arr[3], myValue)
or Volatile.Write(ref arr[3], info);
where arr
is declared as double
?
Real example, I declare double
array like that:
private double[] _cachedProduct;
In one thread I update it like that:
_cachedProduct[instrumentId] = calcValue;
//...
are.Set();
In another thread I read this array like that:
while(true)
{
are.WaitOne();
//...
result += _cachedProduct[instrumentId];
//...
}
For me it just works fine as is. However to make sure "it will always work" no matter what it seems I should add either Volatile.Write
or Interlocked.Exchange
. Because double update is not guaranteed to be atomic.
In the answer to this question I want to see detailed comparison of Volatile
and Interlocked
classes. Why we need 2 classes? Which one and when to use?
Another example, from the implementation of a locking mechanism in an in-production project:
private int _guard = 0;
public bool Acquire() => Interlocked.CompareExchange(ref _guard, 1, 0) == 0;
public void Release1() => Interlocked.Exchange(ref _guard, 0);
public void Release2() => Volatile.Write(ref _guard, 0);
Does it make any practical difference if the users of this API call the Release1
or the Release2
method?
the Interlocked.Exchange uses a processor instruction that guarantees an atomic operation.
The Volatile.Write does the same but it also includes a memory barrier operation. I think Microsoft added Volatile.Write on DotNet 4.5 due to support of ARM processors on Windows 8. Intel and ARM processors differs on memory operation reordering.
On Intel, you have a guarantee that memory access operations will be done in the same order they are issued, or at least that a write operation won't be reordered.
From Intel® 64 and IA-32 Architectures Software Developer’s Manual, Chapter 8:
8.2.2 Memory Ordering in P6 and More Recent Processor Families The Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium 4, and P6 family processors also use a processor-ordered memory-ordering model that can be further defined as “write ordered with store-buffer forwarding.” This model can be characterized as follows.
On ARM you don't have this kind of guarantee, so a memory barrier is required. An ARM blog explaining this can be found here: http://blogs.arm.com/software-enablement/594-memory-access-ordering-part-3-memory-access-ordering-in-the-arm-architecture/
In your example, as the operation with double is not guaranteed to be atomic, I would recommend a lock to access it. Remember that you have to use the lock on both parts of your code, when reading and setting the value.
A more complete example would be better to answer your question, as it is not clear what happens after these values are set. For a vector, if you have more readers than writers, consider the use of a ReaderWriterLockSlim object: http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
The number of threads and the frequency of read/writes can change dramatically your locking strategy.