Reading Joseph Albahari's threading tutorial, the following are mentioned as generators of memory barriers:
lock
statement (Monitor.Enter
/Monitor.Exit
)Interlocked
class In addition, Hans Passant and Brian Gideon added the following (assuming none of which already fits into one of the previous categories):
Thread.Sleep()
I was wondering if this list was complete (if a complete list could even be practically made)
EDIT additions suggested:
In computing, a memory barrier, also known as a membar, memory fence or fence instruction, is a type of barrier instruction that causes a central processing unit (CPU) or compiler to enforce an ordering constraint on memory operations issued before and after the barrier instruction.
Memory barrier is implemented by the hardware processor. CPUs with different architectures have different memory barrier instructions. Therefore, the programmer needs to explicitly call memory barrier in the code to solve the preceding problem.
Memory barrier, also known as membar or memory fence, is a class of instructions which cause a central processing unit (CPU) to enforce an ordering constraint on memory operations issued before and after the barrier instruction.
The answer is: No. A function call is always a compiler barrier. But a function call is not guaranteed to be a memory barrier. It only is if the code in the called function contains a memory barrier.
Here is my take on the subject and to attempt to provide a quasi-complete list in one answer. If I run across any others I will edit my answer from time to time.
Mechanisms that are generally agreed upon to cause implicit barriers:
Monitor
class methods including the C# keyword lock
Interlocked
class methods.Volatile
class methods (.NET 4.5+).SpinLock
methods including Enter
and Exit
.Thread.Join
Thread.VolatileRead
and Thread.VolatileWrite
Thread.MemoryBarrier
volatile
keyword.QueueUserWorkItem
, Task.Factory.StartNew
, Thread.Start
, compiler supplied BeginInvoke
methods, etc.ManualResetEvent
, AutoResetEvent
, CountdownEvent
, Semaphore
, Barrier
, etc.Control.Invoke
, Dispatcher.Invoke
, SynchronizationContext.Post
, etc.Mechanisms that are speculated (but not known for certain) to cause implicit barriers:
Thread.Sleep
(proposed by myself and possibly others due to the fact that code which exhibits a memory barrier problem can be fixed with this method)Thread.Yield
Thread.SpinWait
Lazy<T>
depending on which LazyThreadSafetyMode
is specifiedOther notable mentions:
lock
or Interlocked.CompareExchange
.MarshalByRefObject
seems to suppress certain optimizations in subclasses which may make it appear as if an implicit memory barrier were present. Thanks to Hans Passant for discovering this and bringing it to my attention.1 1This explains why BackgroundWorker
works correctly without having volatile
on the underlying field for the CancellationPending
property.
I seem to recall that the implementations of the Thread.VolatileRead and Thread.VolatileWrite methods actually cause full fences, not half fences.
This is deeply unfortunate, as people might have come to rely upon this behaviour unknowingly; they might have written a program that requires a full fence, think they need a half fence, think they are getting a half fence, and will be in for a nasty surprise if an implementation of these methods ever does provide a half fence.
I would avoid these methods. Of course, I would avoid everything involving low-lock code, not being smart enough to write it correctly in anything but the most trivial cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With