5

Do I need to add a lock block here if I want to be sure that instance will be created only 1 time?

        if (instance==null)
        {
            instance = new Class();
        }

Since there is only 1 instruction within the IF I'm not 100% sure. In a case like below, I'm sure I'd need it but I wanted to double check if the same applies for the code above.

        if (instance==null)
        {
            int i = 5;
            int y = 78;
            instance = new Class(i, y);
        }

EDIT

Yes, I assume multithreading

StackOverflower
  • 5,463
  • 13
  • 58
  • 89
  • 2
    Probably worth looking at [Implementing the Singleton Pattern in C#](http://csharpindepth.com/Articles/General/Singleton.aspx) – Conrad Frix Sep 13 '11 at 20:19

5 Answers5

9

If you are working multithreaded then the answer is YES.

Small note:

Clearly you can't put a lock using instance, because it's null :-)

The Lock paradigm is normally (it's called Double-checked locking):

if (instance == null)
{
    lock (something)
    {
        if (instance == null)
        {
             instance = new Class();
        }
    }
}

If you want (and if creating Class isn't expensive) you can do:

if (instance == null)
{
    Interlocked.CompareExchange(ref instance, new Class(), null);
    // From here you are sure the instance field containts a "Class"
}

The only problem with this piece of code is that two threads could create a new Class(), but only one will be able to set instance with a reference of new Class(), so the other one will have created a useless Class object that will be GC. If creating Class objects is inexpensive (for example creating a List<T>) it's ok. If creating Class objects is expensive (perhaps because the constructor calls a DB an do a big big query) then this method is a no-no.

Small note: the Grinch has arrived and it has declared that all this multi-threading isn't "fool-proof" enough. And he is right. And he is wrong. It's like the Schrödinger's cat! :-) The last time I wrote a multi-threaded program, I lost a week just reading all the literature that there is around the internet. And I still did errors (but ok, I was trying to write lockless MT code... It's quite heavy mumbo jumbo). So use Lazy<T> if you are using .NET 4.0, and if not well... Take the mono source and find where Lazy is defined and copy it :-) (the license is quite permissive, but read it!) BUT If what you need is a singleton, you can use a static Lazy<T> OR if you don't have .NET 4.0 you can use one of the samples from http://www.yoda.arachsys.com/csharp/singleton.html (the fifth one or the fourth one. The author suggest the fourth one. Be aware that there are some interesting caveats on its laziness, but they are written in http://www.yoda.arachsys.com/csharp/beforefieldinit.html). Be aware that you must write them "as written". Don't ever think of deviating from what it's written. DON'T. As you can see reading by the comments, threading is a HARD argument. You can be right and you can still be wrong at the same time. It's like volatile (volatile? Is it a pun?) chemical compounds... Very very funny and very very dangerous.

Another small note: under .NET 1.1 it IS really sticky. You shouldn't share variables between threads in 1.1 unless you know EXACTLY what you are doing. In 2.0 they changed the memory model (how the compiler can optimize access to memory) and they made a "more secure" memory model. .NET 1.1 and older versions of Java (up to the 1.4 included) were a pit trap.


To respond to your question, a simple trick: when you are thinking "can MT break my code?" do this: imagine that Windows (the OS) is a lazy ogre. Sometimes he stops a thread for half an hour while letting the other threads run (technically he can do it. Not for 30 minutes (but there aren't real rules on how much long) but for milliseconds it can and it do if the processor is a little overloaded with work and there are many threads pinned to specific processors (pinned means that they told the OS that they want to run ONLY on some specific processors. So if 1000 threads are pinned on processor 1 and processor 2 has to execute only 1 thread that isn't pinned, it's quite clear that the thread on processor 2 will go much more fast!) ). So imagine that two threads enter your piece of code at the same time, execute the first lines at the same time (they are on two different processors, they can be executed REALLY in parallel), but one of the two threads is stopped and has to wait for 30 minutes. What will happen in the mean time? And note that often code can be stopped in the middle of an instruction! a = a + 1 is two instructions! it's var temp = a + 1; a = temp; If you apply this trick to your example code, it's quite easy to see: both threads execute if (instance==null) and pass, one thread is then stopped for 30 minutes, the other one initializes the object, the first one is resumed and it initializes the object. Two objects initialization. No good :-)

I'll explain the .NET 1.1 problem with a simple example:

class MyClass
{
    public bool Initialized;

    public MyClass()
    {
        Initialized = true;
    }
}

MyClass instance = null;

public MyClass GetInstance()
{
    if (instance == null)
    {
        lock (something)
        {
            if (instance == null)
            {
                instance = new Class();
            }
        }
    }

    return instance;
}

Now... This is the code from before. The problem happens in the instance = new Class() line. Let's split it in the various part:

  1. Space for the Class object is allocated by .NET. The reference to this space is saved somewhere.
  2. The constructors of Class is called. Initialized = true (the field called Initialized!).
  3. The instance variable is set to the reference we saved before.

In the newer "strong" memory model of .NET 2.0 these operations will happen in this order. But let's see what could happen uner .NET 1.1: the writes can be reordered!!

  1. Space for the Class object is allocated by .NET. The reference to this space is saved somewhere.
  2. The instance variable is set to the reference we saved before.
  3. The constructors of Class is called. Initialized = true (the field called Initialized!).

Now let's imagine that the thread doing this is suspended by the OS for 30 minutes after point 2 and before point 3. Another thread could access the instance variable (note that the first if in the code isn't protected by a lock, so it can access it without waiting the first thread to end its work) and use it. But Class isn't really initialized: his constructor hasn't run! Boooom! Had you used the volatile keyword on the declaration of instance (so volatile MyClass instance = null;) this wouldn't have happened, because the compiler can't reorder writes past a write on a volatile field. So he can't reorder the point 2 after the point 3 because in point 3 it's writing on a volatile field. But as I have written, this was a problem of .NET 1.1.

Now. if you want to learn about threading, please read this: http://www.albahari.com/threading/ If you want to know what happens "behind the scenes" you could read http://msdn.microsoft.com/en-us/magazine/cc163715.aspx but it's pretty heavy.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • double checked locking is broken – David Heffernan Sep 13 '11 at 20:19
  • Better to use `Lazy` - it's type safe, and much simpler to get right. – Reed Copsey Sep 13 '11 at 20:20
  • @xanatos: http://www.yoda.arachsys.com/csharp/singleton.html - It'll work on x86/x64, but it's not guaranteed by ECMA CLI spec. – Reed Copsey Sep 13 '11 at 20:22
  • Why would you use Interlocked.Exchange? That doesn't solve anything – Kyle W Sep 13 '11 at 20:22
  • put my entire comment into a search engine. – David Heffernan Sep 13 '11 at 20:23
  • @Kyle Because I wanted to write Interlocked.CompareExchange but here it's 22:24 and I'm a little sleepy :-) – xanatos Sep 13 '11 at 20:24
  • @DavidHeffernan: Many complaints and not _one single_ down-vote (even when such high rep., skilled and qualified, 'example setting' members are participating) - _that_ is the crying shame! Down-voting is a feature for a reason. – Grant Thomas Sep 13 '11 at 20:25
  • 1
    @David http://stackoverflow.com/questions/394898/double-checked-locking-in-net/394932#394932 Now... Skeets in 2008 sayd it DO work, because they changed the memory model of .NET in 2.0, and from what I DO know it's as I've written. The only bad thing it could happen would be an useless lock (it uses a stale value of instance, it enters the lock, it recheck instance and it sees the newer value) – xanatos Sep 13 '11 at 20:25
  • @Mr. Disappointment I prefer to give the person answering opportunity to correct. – David Heffernan Sep 13 '11 at 20:31
  • @DavidHeffernan The system also compensates for that by allowing the vote to be retracted if the post has been edited since it was cast. – Grant Thomas Sep 13 '11 at 20:32
  • @Reed lock and Interlocked.CompareExchange include a Thread.MemoryBarrier(): http://msdn.microsoft.com/en-us/library/ms686355(v=vs.85).aspx – xanatos Sep 13 '11 at 20:33
  • I'll add that in his pdf http://www.albahari.com/threading/part3.aspx Albahari puts a clear example of double locking (look just before Lazy) – xanatos Sep 13 '11 at 20:35
  • @xanatos Jon Skeet's definitive commentary on this is here: http://csharpindepth.com/Articles/General/Singleton.aspx – David Heffernan Sep 13 '11 at 20:40
  • @xanatos: You'd need to have a memory barrier inside of the inner check, for this to be "correct" (or have the variable declared volatile). That being said, it works perfectly fine on x86 & x64, so for practical purposes, its fine. – Reed Copsey Sep 13 '11 at 20:43
  • @David and read the appendix "(I wouldn't use solution 1 because it's broken, and I wouldn't use solution 3 because it has no benefits over 5.)". So in the end it isn't anymore a problem of brokeness of solution 3, it's more a problem of non-efficiency. – xanatos Sep 13 '11 at 20:44
  • 1
    @xanatos It behoves you to point all this out in the answer. This is an incredibly sticky area. Such an answer needs to be incredibly precise. – David Heffernan Sep 13 '11 at 20:50
  • Thanks for your answer! It's really great and I upvoted it myself but IMO @Carl Norum asnwered more clearly WHY I need the lock. That's why I marked his answer. – StackOverflower Sep 13 '11 at 20:52
  • @David I'll add from this [Understand the Impact of Low-Lock Techniques in Multithreaded Apps](http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S10) It's on MSDN, so I think it's as official as you can get: *Technique 4: Lazy Initialization*, same code as mine, *In the .NET Framework 2.0 model, the code works without volatile declarations.* – xanatos Sep 13 '11 at 20:58
  • 6
    @xanatos: That is not the correct line to be quoting from that article. The correct line to be quoting is "**Whenever possible, avoid low-lock techniques.**" You can avoid a low lock technique here. Do so. – Eric Lippert Sep 13 '11 at 21:29
  • @eric So we should live of fears? A piece of code is correct or it isn't correct. I'll add to that sentence "whenever possible, avoid sharing fields between multiple threads because you'll fail". If this is the mood, then this is the only reality. Because in the end, even if you the programmer knows of Lazy, he still doesn't probably know of the intricacies of volatile or all the other things that are waiting in the dark to prey on him (for those interested http://www.albahari.com/threading/part4.aspx under `The volatile keyword` the second red box. – xanatos Sep 14 '11 at 04:59
  • @xanatos: Absolutely, I live in fear of low-lock code. I don't know *nearly* enough about memory models in order to *know* whether a given piece of code is correct or not. As one of my managers used to say "would you bet your car on this code being correct?" I don't bet my car on low-lock code. – Eric Lippert Sep 14 '11 at 05:27
  • @Eric I wouldn't bet my car on simple T-SQL code. Should I not write simple T-SQL code? In the end programming isn't an exact science. And even if you are the god of programmers, the programmer near you perhaps just finished the college. – xanatos Sep 14 '11 at 05:32
  • @xanatos - But if you can achieve much greater confidence of correctness by using a lock, then why advocate for low lock techniques? The type of code your suggesting is notoriously difficult to reason about (even for experts) - why use it if there's a simpler approach that works? – kvb Sep 14 '11 at 13:45
  • @kvb Please, look at the "suggested" (in http://www.yoda.arachsys.com/csharp/singleton.html) (suggested by Reed and by Skeet in the other link) at the very beautiful singleton classes (fourth example) and, without watching the MSDN, tell me what this sentence mean and when it could happen: `The laziness of type initializers is only guaranteed by .NET when the type isn't marked with a special flag called beforefieldinit.` You know, before studying these wonderful singletons I hadn't ever read of the special flag called beforefieldinit. (the author of that page suggest option 4!) – xanatos Sep 14 '11 at 13:51
  • @xanatos - I'm not suggesting that other approaches are _easy_ to reason about, but I think that many people's experiences have shown that low lock techniques are _even harder_ to reason about. Here, that fact isn't outweighed by any compelling benefit. – kvb Sep 14 '11 at 14:09
  • @kvb So: you (not you you, you someone) don't know how the double locking work (BUT there are many persons that, having badly read the essay of a person about .NET 1.1 think that it's bad), you don't know how or why the static initializer work BUT you don't want to use the lock construct because you don't know what it's AND you choose the static constructor initializer just because you think you know what a static constructor initializer is and how it works. Is this an informed choice? I think it's piling on ignorance and superstition. – xanatos Sep 14 '11 at 14:16
  • @kvb And as a sidenote, the examples of the link are for **static singletons**, you know, like **static Lazy** and not for **Lazy**. Now try doing it for a Lazy. Because that page is for the singleton pattern :-) – xanatos Sep 14 '11 at 14:18
  • @xanatos - Locking is easier to understand and make correct than techniques which don't use locking (e.g. via the Interlocked class), and using something like Lazy is easier still. Even when considering uses of locking, though, not all implementations are created equal - the "double checked" idiom is harder to understand and make correct than just putting the lock on the outside, and in many cases the perf difference is likely to be inconsequential. The code is obviously correct when the lock is always taken - why deviate from this pattern if there isn't a measurable perf problem? – kvb Sep 14 '11 at 15:13
  • @kvb Even what you propose doesn't work on .NET 1.1 without the `volatile` keyword. Should I write the best code I can or the most easy code I can? Do you think I should lobotomize myself before responding on SO? :-) :-) Every technique is "pure magic" unless you know it. Lambda expressions are pure magic until you learn what they are. No, the double lock isn't more complex. It's copy'n'paste in a getter, like the other singleton examples. You think you could rebuild from memory the 5th example from that link? Easier than double checked locking? – xanatos Sep 14 '11 at 15:18
8

If this is multithreaded, then yes, you need a lock or some form of synchronization.

However, if this is to allow lazy instantiation, I'd suggest using Lazy<T>. It handles the thread safety for you, and eliminates the need for the check.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • @Timmy If I remember correctly, Lazy was introduced in the Reactive Framework with a big part of the Concurrent collections they introduced in C# 4.0 – xanatos Sep 13 '11 at 20:37
  • @Timmy: It is part of that framework. Thats' a great way to get it and the TPL for .NET 3.5. – Reed Copsey Sep 13 '11 at 20:41
7

Yes, you need a lock in both of your two examples. Let's number the lines to make the explanation easier:

1 if (instance == null)
2 {
3     instance = new Class();
4 }

Now, assume you have two threads, A and B. Both threads are executing this code. First, A tests instance at line 1, and since it's null it takes the true path - on to line 3. Then, you get a context switch before line 3 is executed and B does the same (true on line 1 and ends up at line 3). Now both threads are inside your if statement body, and you'll get two assignments to instance.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
2

If you're using multiple threads, yes. Multiple threads could enter the if statement before any of them actually set the instance to the new class.

Kyle W
  • 3,702
  • 20
  • 32
1

you need to read this article:

Implementing the Singleton Pattern in C#

and this question as well:

Is this Singleton implementation correct and thread-safe?

good luck :)

Community
  • 1
  • 1
Davide Piras
  • 43,984
  • 10
  • 98
  • 147
  • who is downvoting me and why!??!? – Davide Piras Sep 13 '11 at 20:23
  • +1 clearly double checked locking and erroneous interlocked calls is the rout to upvotes – David Heffernan Sep 13 '11 at 20:25
  • Because 'nothing but a link' answers are actually a [notable problem](http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers/8259#8259). – Grant Thomas Sep 13 '11 at 20:40
  • 1
    While this may theoretically answer the question, [it would be preferable](http://meta.stackexchange.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Bill the Lizard Sep 14 '11 at 11:30