none
Interlocked.CompareExchange RRS feed

  • Question

  • Hi,
    Just was looking at .net framework source code and found it interesting how syncRoots are created there:

        get {
            if( _syncRoot == null) {
                System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);
            }
            return _syncRoot;
        }

    Technically, this means that a lot of syncRoots can be created, as far as we don't double check. So, first question is why several sync roots is not a problem?

    Next. Apparently we can reuse this technique to avoid locks in Singletons as well - if we could handle multiple signletons creation 

        get {
            if( _instance == null) {
                var singleton = new Singleton();
                if(System.Threading.Interlocked.CompareExchange(ref _instance, singleton, null) != null) {
                    singleton.Dispose(); //if needed
                }
            }
            return _instance;
        }

    I did a quick benchmarking (code below) that does show a tiny speedup.
    So, next question is what do you think about such usage of Interlocked.CompareExchange?

    Here's the benchmarking code (and the last question, is there a better way to benchmark threading code ):

    namespace Performance
    {
        public class ClassicSingleton<T>
        {
            private volatile static ClassicSingleton<T> _instance;
            private static readonly object sync = new object();

            public static ClassicSingleton<T> Instance
            {
                get
                {
                    if (_instance == null) {
                        lock (sync) { if (_instance == null) { _instance = new ClassicSingleton<T>(); } }
                    }
                    return _instance;
                }
            }
        }

        public class InterlockedSingleton<T>
        {
            private static InterlockedSingleton<T> _instance;

            public static InterlockedSingleton<T> Instance
            {
                get
                {
                    if (_instance == null) {
                        var singleton = new InterlockedSingleton<T>();
                        if (System.Threading.Interlocked.CompareExchange(ref _instance, singleton, null) != null) {
                            //singleton.Dispose();
                        }
                    }
                    return _instance;
                }
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                // As far as static fields are not shared...

                var sw = new Stopwatch();
                sw.Start();
                var valint = ClassicSingleton<int>.Instance;
                var valbool = ClassicSingleton<bool>.Instance;
                var valfloat = ClassicSingleton<float>.Instance;
                var valdatetime = ClassicSingleton<DateTime>.Instance;
                var valguid = ClassicSingleton<Guid>.Instance;
                sw.Stop();
                Console.WriteLine(sw.ElapsedMilliseconds + " ms");

                var sw2 = new Stopwatch();
                sw2.Start();
                var valint2 = InterlockedSingleton<int>.Instance;
                var valbool2 = InterlockedSingleton<bool>.Instance;
                var valfloat2 = InterlockedSingleton<float>.Instance;
                var valdatetime2 = InterlockedSingleton<DateTime>.Instance;
                var valguid2 = InterlockedSingleton<Guid>.Instance;
                sw2.Stop();
                Console.WriteLine(sw2.ElapsedMilliseconds + " ms");
            }
        }
    }
    Sunday, April 12, 2009 1:17 AM

Answers

  • Hi,
    Just was looking at .net framework source code and found it interesting how syncRoots are created there:

        get {
            if( _syncRoot == null) {
                System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);
            }
            return _syncRoot;
        }

    Technically, this means that a lot of syncRoots can be created, as far as we don't double check. So, first question is why several sync roots is not a problem?



    What do you mean alot of syncroots can be created? Whoever calls this code will only ever get a reference to a single object as syncroot. Incase two threads create a race condition and ask for the syncroot at exact the same moment and therefore both enter the  if( _syncRoot == null) part the Interlocked.CompareExchange will make sure only one of the two will actually succed. 

    • Marked as answer by Bin-ze Zhao Tuesday, April 14, 2009 10:08 AM
    Sunday, April 12, 2009 2:26 AM
  • I Really can't give you any insight why the framework is written the way it is. However you can't really compare the creation of a synclock with the creation of a singleton, if you're creating a synclock it doesn't really matter if an object is created multiple times incase of a race condition you're just newing up an object fairly cheap operation. Creating a singleton can be alot more expensive with perhaps connections to a database and then its suddenly really undesireable to create it like this.
    • Marked as answer by Bin-ze Zhao Tuesday, April 14, 2009 10:14 AM
    Monday, April 13, 2009 5:19 PM

All replies

  • Hi,
    Just was looking at .net framework source code and found it interesting how syncRoots are created there:

        get {
            if( _syncRoot == null) {
                System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);
            }
            return _syncRoot;
        }

    Technically, this means that a lot of syncRoots can be created, as far as we don't double check. So, first question is why several sync roots is not a problem?



    What do you mean alot of syncroots can be created? Whoever calls this code will only ever get a reference to a single object as syncroot. Incase two threads create a race condition and ask for the syncroot at exact the same moment and therefore both enter the  if( _syncRoot == null) part the Interlocked.CompareExchange will make sure only one of the two will actually succed. 

    • Marked as answer by Bin-ze Zhao Tuesday, April 14, 2009 10:08 AM
    Sunday, April 12, 2009 2:26 AM
  • Ray thanks for pointing out the confusion.

    "Incase two threads create a race condition and ask for the syncroot at exact the same moment and therefore both enter the  if( _syncRoot == null) part the Interlocked.CompareExchange will make sure only one of the two will actually succeed"


    Interlocked.CompareExchange will make sure only one of the two will actually swap . New objects will still get created anyway. As many threads step on that, so many new objects get created.

    So - only one sync root, many garbage objects, sorry for confusion.

    Hence my questions in the original post - "if we could handle multiple signletons creation" and below - because in that Singletons sample we're potentially creating several garbage singleton objects.
    Sunday, April 12, 2009 9:48 AM
  • Why not just move the creation problem into the framework?

    		public class MyClass
    		{
    			private static readonly MyClass _instance = new MyClass();
    
    			public static MyClass Instance
    			{
    				get
    				{
    					return _instance;
    				}
    			}
    
    			private MyClass()
    			{
    			}
    		}
    The runtime will make sure the static instance will only be created once and the private contsructor keeps other people from creating instances.
    Monday, April 13, 2009 4:40 PM
  • That's true - no objection - but why the framework itself is written in that fashion? Look at System.Console implementation:

            private static Object s_InternalSyncObject;
            private static Object InternalSyncObject {
                get {
                    if (s_InternalSyncObject == null) {
                        Object o = new Object();
                        Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
                    }
                    return s_InternalSyncObject;
                }
            }

    Why not just private static Object s_InternalSyncObject = new object() ??

    I guess it's something to do with static field initialization (and order?), but it doesn't explain why the same approach is used for non-static properties as well - say, Hashtable implementation:

            public virtual Object SyncRoot {
                get {
                    if( _syncRoot == null) {
                        System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);   
                    }
                    return _syncRoot;                
                }
            }


     
    Monday, April 13, 2009 5:13 PM
  • I Really can't give you any insight why the framework is written the way it is. However you can't really compare the creation of a synclock with the creation of a singleton, if you're creating a synclock it doesn't really matter if an object is created multiple times incase of a race condition you're just newing up an object fairly cheap operation. Creating a singleton can be alot more expensive with perhaps connections to a database and then its suddenly really undesireable to create it like this.
    • Marked as answer by Bin-ze Zhao Tuesday, April 14, 2009 10:14 AM
    Monday, April 13, 2009 5:19 PM
  • Absolutely agree.

    I was just amazed seeing those CompareExchanges instead of a mere "object syncRoot = new object()" and was trying to blind guess a reason - at the same time coming up with ideas on how to reuse it :)
    Monday, April 13, 2009 6:11 PM