The need for a volatile modifier for a double checked lock in .NET.

In several texts, it is said that when implementing a double-check lock in .NET, a variable modifier should be used in the field in which you are locking. But why exactly? Consider the following example:

public sealed class Singleton { private static volatile Singleton instance; private static object syncRoot = new Object(); private Singleton() {} public static Singleton Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) instance = new Singleton(); } } return instance; } } } 

why does "lock (syncRoot)" not perform the required memory sequence? Is it not true that after the word β€œblocking” both reading and writing would be unstable, and therefore the necessary consistency would be ensured?

+74
c # volatile singleton
Dec 27 '09 at 0:13
source share
8 answers

Volatile are not needed. Well, like **

volatile used to create a memory barrier * between reading and writing a variable.
lock , when used, creates memory barriers around the block inside the lock , in addition to restricting access to the block in a single thread.
Memory items make sure that each thread reads the most current value of the variable (not a local value cached in some register), and that the compiler does not reorder the statements. Using volatile not required ** because you already have a lock.

Joseph Albahari explains this material better than ever.

And don't forget to check out Jon Skeet 's singleton implementation guide in C #


<b> Update:
* volatile causes the VolatileRead variable to be read and is written as VolatileWrite s, which on x86 and x64 on the CLR is implemented using the MemoryBarrier . They may be finer in other systems.

** My answer is correct only if you use the CLR for x86 and x64 processors. This may be true in other memory models such as Mono (and other implementations), Itanium64, and future hardware. This is what John talks about in his "gotchas" article for double-checking locks.

Performing one of the {labeling of the volatile variable, reading it with Thread.VolatileRead or inserting a call to Thread.MemoryBarrier } may be required for the code to work correctly in a situation of a model with weak memory.

From what I understand, on the CLR (even on IA64), records are never reordered (records always have release semantics). However, on IA64, reading can be reordered to arrive before writing if they are not labeled volatile. Unfortunately, I do not have access to the IA64 equipment for the game, so everything I say about this would be an assumption.

I also found these articles helpful:
http://www.codeproject.com/KB/tips/MemoryBarrier.aspx
vance morrison article (all links to this indicate a double checked lock)
article by chris brumme (all links to this)
Joe Duffy: Broken Double Proven Blocking Options

The luis abreu multithreading series gives a good overview of concepts too
http://msmvps.com/blogs/luisabreu/archive/2009/06/29/multithreading-load-and-store-reordering.aspx
http://msmvps.com/blogs/luisabreu/archive/2009/07/03/multithreading-introducing-memory-fences.aspx

+57
Dec 27 '09 at 1:06
source share
β€” -

There is a way to implement it without a volatile field. I will explain it ...

I think this is a reordering of memory access inside the lock, which is dangerous, so you might get a not fully initialized instance outside the lock. To avoid this, I do this:

 public sealed class Singleton { private static Singleton instance; private static object syncRoot = new Object(); private Singleton() {} public static Singleton Instance { get { // very fast test, without implicit memory barriers or locks if (instance == null) { lock (syncRoot) { if (instance == null) { var temp = new Singleton(); // ensures that the instance is well initialized, // and only then, it assigns the static variable. System.Threading.Thread.MemoryBarrier(); instance = temp; } } } return instance; } } } 

Code understanding

Imagine that there is some initialization code inside the constructor of the Singleton class. If these instructions are reordered after the field is specified with the address of the new object, then you have an incomplete instance ... imagine that the class has this code:

 private int _value; public int Value { get { return this._value; } } private Singleton() { this._value = 1; } 

Now imagine calling the constructor using the new statement:

 instance = new Singleton(); 

This can be extended to the following operations:

 ptr = allocate memory for Singleton; set ptr._value to 1; set Singleton.instance to ptr; 

What if I reorder these instructions as follows:

 ptr = allocate memory for Singleton; set Singleton.instance to ptr; set ptr._value to 1; 

Does it really matter? NO if you are thinking of a single thread. YES if you are thinking of multiple threads ... what if a thread is interrupted immediately after set instance to ptr :

 ptr = allocate memory for Singleton; set Singleton.instance to ptr; -- thread interruped here, this can happen inside a lock -- set ptr._value to 1; -- Singleton.instance is not completelly initialized 

This is what prevents the memory barrier by not reordering memory access:

 ptr = allocate memory for Singleton; set temp to ptr; // temp is a local variable (that is important) set ptr._value to 1; -- memory barrier... cannot reorder writes after this point, or reads before it -- -- Singleton.instance is still null -- set Singleton.instance to temp; 

Happy coding!

+30
Oct 18
source share

I don’t think anyone really answered the question, so I’ll try.

Volatile and first if (instance == null) are not "necessary". Blocking will make this code thread safe.

So the question is: why are you adding the first if (instance == null) ?

The reason, apparently, is to avoid unnecessarily executing a blocked section of code. While you are executing code inside the lock, any other thread that also tries to execute this code is blocked, which will slow down your program if you try to access the singleton often from many threads. Depending on the language / platform, there may also be overhead from the castle itself that you want to avoid.

So, the first zero check is added as a very quick way to see if you need a lock. If you do not need to create a singleton, you can completely lock the lock.

But you cannot check if the link is null without blocking it in any way, because due to caching of the processor, another thread may change it, and you will read the "obsolete" value, which will lead you to unnecessarily enter the lock, But You are trying to avoid blocking!

So you are doing singleton volatile to make sure you read the last value, without having to use a lock.

You still need an internal lock, because volatile protects you with only one access to the variable β€” you cannot safely test and set it without using a lock.

Now, is this really helpful?

Well, I would say "in most cases, no."

If Singleton.Instance can cause inefficiencies due to locks, why do you call it so often that this will be a serious problem? The whole point of singleton is that there is only one, so your code can read and cache a singleton link once.

The only time I can think about where this caching would be impossible is when you have a large number of threads (for example, a server using a new thread to process each request can create millions of very short threads, each of which should have would call Singleton.Instance once).

Thus, I suspect that a double-checked lock is a mechanism that has a real place in very specific cases with critical criticism, and then everyone climbed to "this is the right way to do this," winning, without actually thinking about what he was doing and whether it will really be necessary if they use it for.

+7
Dec 27 '09 at 8:55
source share

AFAIK (and - take this with care, I don't do many parallel things) no. Blocking just gives you synchronization between multiple opponents (threads).

volatile, on the other hand, says that your machine re-evaluates the value each time, so that you don't run across a cached (and incorrect) value.

See http://msdn.microsoft.com/en-us/library/ms998558.aspx and pay attention to the following quote:

In addition, the variable is declared volatile to ensure that the assignment of the instance variable is completed before access to the instance variable is available.

Volatile Description: http://msdn.microsoft.com/en-us/library/x13ttww7%28VS.71%29.aspx

+3
Dec 27 '09 at 0:20
source share

You should use volatile with a double check lock pattern.

Most people point to this article as proof that you don't need volatility: https://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S10

But they don’t read to the end: β€œ The final word of the warning - I only guess about the x86 memory model from the observed behavior on existing processors. Thus, low-locking methods are also fragile because hardware and compilers can become more aggressive over time time. minimize the impact of this fragility on your code. First, avoid low-locking methods whenever possible. (...) Finally, suppose that the weakest memory model is possible using mutable declarations and, instead of relying on the implicit guarantee.

If you need more conviction, then read this article about the ECMA specification, which will be used for other platforms: msdn.microsoft.com/en-us/magazine/jj863136.aspx

If you need to convince even more, read this new article about how optimization can be optimized so that it does not work without mutability: msdn.microsoft.com/en-us/magazine/jj883956.aspx

In general, this may "work" for you without volatility at the moment, but does not allow it to write the correct code and use volatile or volatileread / write. Articles that propose doing differently sometimes leave some of the possible risks of JIT / compiler optimizations that might affect your code, as well as future optimizations that might happen that could break your code. Also, as mentioned in a previous article, the assumption that work without volatility can no longer be performed, ARM can not take.

+3
Feb 23 '15 at 4:18
source share

Enough lock . The specifics of MS (3.0) itself mentions this exact scenario in Β§8.12 without mentioning volatile :

The best approach is to synchronize access to static data by locking a private static object. For example:

 class Cache { private static object synchronizationObject = new object(); public static void Add(object x) { lock (Cache.synchronizationObject) { ... } } public static void Remove(object x) { lock (Cache.synchronizationObject) { ... } } } 
+2
Dec 27 '09 at 9:19
source share

I think I found what I was looking for. Details in this article are http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S10 .

To summarize - in .NET volatile the modifier is really not needed in this situation. However, with weaker memory models, records made in the constructor of a lazily-initiated object can be delayed after writing to the field, so other threads can read a corrupt non-zero instance in the first if statement.

+2
Dec 27 '09 at 22:56
source share

This is a pretty good post about using volatiles with double checked lock:

http://tech.puredanger.com/2007/06/15/double-checked-locking/

In Java, if the goal is to protect a variable, you don't need to block if it is marked as modified

-2
Dec 27 '09 at 0:19
source share



All Articles