V
V
Vadim Nikiforov2022-02-10 10:16:19
.NET
Vadim Nikiforov, 2022-02-10 10:16:19

Is it expensive to use lock, why do Singleton with double check locking?

A person with a very large experience (about 20 years) of programming (senior) once explained to me that lock is a rather expensive operation, because under the hood, lock = try + Monitor.Enter() + finally + Monitor.Release() and it's "very expansive" and so we do like this:

if (_ht == null) 
{
    lock (_sync) 
    {
         Create();
    }
}

Maybe the check could be done in the Create() method? And not before lock?
But somehow I was interviewed in one company for the position of middle + / senior developer and their senior and team lead told me that lock is one of the cheapest synchronization operations.

So my first question is, who was right? And who led me astray?

Therefore, I have the following question, why do we need to do Singleton with double check locking?

public sealed class Singleton1
    {
        private static volatile Singleton1 _instance;

        //locker
        private static readonly object Loker = new();

        public static Singleton1 Instance
        {
            get
            {
                //first check
                if (_instance == null)
                {
                    // another Thread may create instance
                    lock (Loker) // very expensive, because - lock = try + Monitor.Enter() + finally + Monitor.Release()
                    {
                        //second check
                        if (_instance == null)
                        {
                            _instance = new Singleton1();
                        }
                    }
                }

                return _instance;
            }
        }
    }

Maybe one check is enough, something like this?

public sealed class Singleton1
    {
        private static volatile Singleton1 _instance;

        //locker
        private static readonly object Loker = new();

        public static Singleton1 Instance
        {
            get
            {
                  lock (Loker)
                  {
                        // only one check and everything is fine :)
                      if (_instance == null)
                      {
                          _instance = new Singleton1();
                      }
                }

                return _instance;
            }
        }
    }

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
Vasily Bannikov, 2022-02-10
@nikifovadim

why do Singleton with double check locking

It makes sense to do a singleton only when (all three):
1. It’s not a fact that during the application’s operation, an instance of this class will be needed (otherwise we’ll destroy it through a regular static one)
2. It’s very expensive to create it, and its instance requires a lot of resources (memory , or maybe some unmanaged ones), so it also needs to be reused everywhere. (otherwise, why in principle a singleton?)
3. There is no way to resolve it at the Dependency Injection level (otherwise, we resolve it through DI)
Double check-lock is necessary to ensure that only 1 singleton instance will be created.
It is considered that creating a singleton instance is more expensive than 1 lok.
A check before the lock is needed then, so as not to block once again.
they said that lock is one of the cheapest synchronization operations.

Yes it is. If much more expensive operation.
get
            {
                  lock (Loker)
                  {
                        // only one check and everything is fine :)
                      if (_instance == null)
                      {
                          _instance = new Singleton1();
                      }
                }

                return _instance;
            }

Why do you need Lock if you are not going to change the variable?
You first check for null to see if you have to change it.
Then you raise the lock and check again to make sure you're the only one accessing it.
You don't need a lock to read.
Checking for null is much cheaper than lock, so it makes sense to check for null before raising the lock

A
AndromedaStar, 2022-02-10
@AndromedaStar

lock is a super cheap operation. Mutex opening/closing - 25 ns. I'm afraid that checking for null-pointer is more expensive, but it's an interesting question. If you figure out this question, which is faster - it will be cool to know.
But in the context of your question, the lock overhead is absolutely ridiculous, since the overhead would be 50 - 100 ns. This is comparable to accessing RAM.
By the way, I often came across in social services that people think that the mutex is implemented somehow programmatically at the OS level or that some kind of magic happens in the virtual machine. But no, it's implemented right at the hardware level, which is why it's so fast.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question