user981225 user981225 - 1 year ago 127
C# Question

Interlocked.Exchange clarification

I have a few simple (hopefully) questions that I have been unable to find answers for -

Say I have objects a, b that are accessible to multiple threads.

Interlocked.Exchange(ref a, b)

If 'b' is not volatile, will this operation treat it as such? i.e. will it take the latest value of this variable from memory? If so, is that read 'atomic' with the write? I understand that the main purpose of Interlocked.Exchange is that you will get the previous value of 'a' as an atomic operation with the new write. But my main confusion is around what value of 'b' is actually written to 'a'.

My second question is related to a quote in this article:

"One interesting point is that all writes in C# are volatile according to the memory model as documented here and here, and are also presumably implemented as such. The ECMA specification of the C# language actually defines a weaker model where writes are not volatile by default."

Is this true? If so, is there a purpose of Interlocked.Exchange if one doesn't care about the previous value of 'a'? (pertaining to my first example). I don't see any other articles or comments on StackOverflow around every write being volatile. I understand, however, that writes are atomic.

Edit: If the answer to my first question is that 'b' is not treated as volatile, and the answer to my second question is that writes are indeed volatile, then a follow up is, when is interlocked.exhange useful if we don't care about the previous value of 'a'?

Answer Source

the variable passed to Exchange (or any volatile variable passed to any method) does not retain "volatility" when passed... There's actually no need for it to be volatile (for the duration of the method call) because the only thing that volatile does is to make sure that the compiler(s) do not optimize use of the variable (which generally means optimizing writes into registers so the value can only be "seen" by a single processor). On processors other than x86/x64, this sometimes means instructions that guarantee acquire or release semantics. .NET doesn't use registers for argument passing, so volatile couldn't affect "volatility" of the arguments passed. It has to always get the latest value from memory due to visibility guarantees of the memory model

RE question 2: the quote is "sort of" true, depending on the declaration of a field, there are visibility guarantees w.r.t. fields; but without "volatile" field access can be optimized into a register in certain phases of use, potentially hiding certain writes from other processors.

Interlocked exchanges make operations that were not atomic appear atomic. Exchange by nature is similar to:

var x = someVariable;
someVariable = y;

This cannot be atomic regardless of the type of someVariable. Exchange makes this operation atomic. This is also atomic with non-atomic types like double, long (in 32-bit), etc.

Part of what Exchange does to make this atomic is to use memory fences--which make writes visible and not re-ordered with reads of the same memory address in the sequence of instructions after the memory fence.

Why would you use Exchange if you don't care about the previous value of 'a'? If you don't care about an actual "exchange", then VolatileWrite seems more appropriate.

Alternatively, if "exchange" is not needed, you could write thread-safe code to model "A=B" as follows:


FWIW, Interlocked is partially modelled around compare-and-swap (CAS) instructions in some processors. These instructions allow you to do these two operations in one instruction (making it atomic). Without things like Interlocked it could be difficult for a compiler to infer that one of these CAS instruction should be used . In addition, Interlocked provides atomic usage on processors that don't support these CAS instructions (and other potentially non-atomic instructions like inc and dec--that may not be available on all processors)