5

Framework: .net 4.5

I am using below sample code pattern to initialize variables in thread safe manner. Recently I have been reading some articles which explains 'double checked locking has been broken in some platforms http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html'. But looks like it's OK for me as I am using .net 4.5.

Recommendation as per the comments

Recommendation is to use lazy<T> and let the .net framework do heavy lifting of handling thread safety and memory models based on platforms: http://msdn.microsoft.com/en-us/library/dd642331.aspx

Update

It appears Eric Lippert has been recommending not to use this pattern at all (now am confused) Name for this pattern? (Answer: lazy initialization with double-checked locking) C# manual lock/unlock

Update 2

Following excerpt is "Like all techniques that remove read locks, the code in Figure 7 (similar to the code I have) relies on strong write ordering. For example, this code would be incorrect in the ECMA memory model unless myValue was made volatile because the writes that initialize the LazyInitClass instance might be delayed until after the write to myValue, allowing the client of GetValue to read the uninitialized state. In the .NET Framework 2.0 model, the code works without volatile declarations." From http://msdn.microsoft.com/en-us/magazine/cc163715.aspx

And I am also not using 'volatile' as many examples showed in different code snippets. I am assuming it's OK too (reference: The need for volatile modifier in double checked locking in .NET )

Pseudo-code that explains the version I am using - built on top of .net 4.5:

static private object s_syncObject = new object();
private static string s_lazyInitializedVariable = null;
//is it necessar to make the backing varible volatie?
//private static volatile string s_lazyInitializedVariable = null;
private static string LazyInitializedVariable
{
    get
    {
        if(string.IsNullOrWhiteSpace(s_lazyInitializedVariable))
        {
            lock(s_syncObject)
            {
                if (string.IsNullOrWhiteSpace(s_lazyInitializedVariable))
                {
                    /*
                        * my lazy initialization code
                        */
                    s_lazyInitializedVariable = "Initialized";
                }
            }
        }
        return s_lazyInitializedVariable;
    }
}

I am seeking confirmation of the same. Basically below are my assumptions as I am using .net 4.5

  1. I am assuming it is OK to use the below code and ignore volatile statement? please ratify if my assumptions are OK.

Note: I also noticed that I can make use of lazy<T> introduced in .net 4.0. but as of now will live with the way I am doing I guess, as I have seen the implementation of lazy<T> using ILSpy, and looks like its doing relatively more stuff for simple tasks like mine.

halfer
  • 19,824
  • 17
  • 99
  • 186
Dreamer
  • 3,371
  • 2
  • 34
  • 50
  • 1
    About your note: you really should consider using it. Just like Tasks do more stuff than manual thread management _in a simple case_, it's just a better way of doing things. – ken2k Jun 19 '14 at 11:29
  • 3
    The code you posted looks right, but that's the tricky thing ... it *looks* right. I'd rather use `Lazy` and *know* that it's right. But if you want to do things the hard way, knock yourself out. – Jim Mischel Jun 19 '14 at 12:14
  • @Jim: I have already used following code snippet in multiple places - I may be able to use 'lazy' for future code, but I would prefer not to change old code if it is guaranteed to be working - but as you said, looks like that's the tricky thing - to know whether it works or not. – Dreamer Jun 19 '14 at 12:18
  • 1
    1) I strongly agree with Jim Mischel. If possible at all, use `Lazy`. Multi threading is tricky enough without getting into all that subtle memory model stuff. 2) This code violates the single responsibility principle since the code is responsible for thread safe initialization in the abstract and for your concrete class. If you need to support .NET 2.0, you should still encapsulate this logic into a single reusable method or class. 3) Why would you use `string.IsNullOrWhiteSpace`? That's confusing, since it makes your code look like the empty string can occur as an uninitialized value. – CodesInChaos Jun 19 '14 at 12:21
  • 1
    @All: looks like the consensus is to use lazy - I will use then and let the framework do the heavy lifting for me:) – Dreamer Jun 19 '14 at 12:26
  • 8
    The fact that you can't look at what is basically *four lines* of code and decide whether it is correct or not is precisely why you should not use this pattern. The relevant question is not "is this safe?" but rather **do you have empirical evidence that you have unacceptably bad performance due to contention on the lock, and furthermore, that the problem can only be solved with a dangerous low-lock solution?** Uncontended locks are a dozen nanoseconds; if you're contending the lock then **attack the contention problem**. – Eric Lippert Jun 19 '14 at 23:25
  • @Eric - gr8 explanation thank you. I am convinced, and already changed code to use Lazy. Best Regards. – Dreamer Jun 19 '14 at 23:40

2 Answers2

1

Static initialization in the CLR is guaranteed to be thread-safe. Either of the following would be fine for thread-safe initialization:

static string s_lazyInitializedVariable = "Initialized";

If you want it lazy, just use the Lazy class:

static Lazy<string> s_lazyInitializedVariable = new Lazy<string>(() => "Initialized");
Dave Black
  • 7,305
  • 2
  • 52
  • 41
  • The static isn't in question, I believe what is in question is the check, lock, check... – Peter Ritchie Sep 26 '14 at 19:12
  • Which is the double-check locking pattern. Which *is* safe in .NET; but easy to get wrong and clearly easy to misunderstand. – Peter Ritchie Sep 26 '14 at 19:14
  • IIRC, Jeff Richter's CLR Internals book addresses the double-check lock pattern and says it is unnecessary for x86 and x64 architectures. I'll have to check though as my books are still packed from moving. – Dave Black Sep 28 '14 at 01:25
  • Yes, there are better ways of doing this. And as Eric says much more understandable patterns. But, we don't just write code for x86/x64... – Peter Ritchie Sep 28 '14 at 01:32
  • Hi Peter, my interpretation was the OP was asking if he was doing it correctly. My answer was to point out a method that is preferred and easier. Given that I don't know the OP's architecture he's writing for, I can say that in 20 years of dev work (obviosuly incl. pre-.NET) I've never worked on a project that was for anything other than x86/x64. I guess I just haven't seen it yet :) – Dave Black Sep 29 '14 at 16:12
  • With things like Store and Phone apps, it's easy to need to support more than x86/x64. Those will be converted to those platforms in the Store, so it's best to not assume x86. But, if you're doing nothing for the Store, keep what works :) I have not encountered a situation where assuming x86 made for a meaningful optimization. And yes, I wouldn't use double-checked locking at all. – Peter Ritchie Sep 29 '14 at 16:17
  • Good point about Phone/Mobile/Store apps - hadn't considered those. They haven't been in my project work yet so it's easy to forget about those :) – Dave Black Sep 30 '14 at 14:58
0

Take a look an this article about singletons in C# : http://www.yoda.arachsys.com/csharp/singleton.html

It explores different techniques (including the double check lock) and their implications, and recommends this one:

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}
vtortola
  • 34,709
  • 29
  • 161
  • 263
  • Thanks for the reply - but please note that I am not trying to create singleton (although it is one of the generic use cases of this pattern) - basically my question is the "idiom" I showed is broken or not for .net 4.5? (as per the links I showed in the question it is broken based on platform - for ex, java without using volatile etc.) – Dreamer Jun 19 '14 at 11:54