Задать вопрос
@nikifovadim
Software Engineer

Дорого ли обходиться использовавние lock, зачем делать Singleton with double check locking?

Мне как-то объяснил человек с очень большим опытом (около 20 лет) программирования (senior), что lock - это довольно дорогая операция, потому что под капотом у lock = try + Monitor.Enter() + finally + Monitor.Release() и это "very expansive" и поэтому мы делаем вот так:

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

Может проверку можно было сделать в методе Create()? А не перед lock?
Но как-то я проходил интервью в одну компанию на позицию middle+/senior разработчика и мне их senior и team lead сказали, что lock это одна из самых дешевых оперций по синхронизации.

Так вот мой первый вопрос, а кто был прав? А кто ввел меня в заблуждение?

Следовательно у меня появился следующий вопрос, а зачем нам делать 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;
            }
        }
    }

Может быть достаточно и одной проверки как то вот так?

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;
            }
        }
    }
  • Вопрос задан
  • 356 просмотров
Подписаться 1 Сложный Комментировать
Решения вопроса 1
зачем делать Singleton with double check locking

Синглтон имеет смысл делать только тогда, когда (все три):
1. Не факт, что за время работы приложения, понадобится экземпляр этого класса (иначе разруливаем через обычный static)
2. Создавать его очень дорого, и его экземпляр требует много ресурсов (памяти, или может каких-то неуправляемых), по тому его нужно ещё и переиспользовать везде. (иначе зачем в принципе синглтон?)
3. Нет возможности разрулить это на уровне Dependency Injection (иначе разруливаем через DI)

Double check-lock необходим, чтобы гарантировать, что экземпляр синглтона будет создан только 1.
Считается, что создавать экземпляр синглтона дороже, чем 1 лок.
А проверка перед локом нужна затем, чтобы не блокировать лишний раз.
сказали что lock это одна из самых дешевых оперций по синхронизации.

Да, это так. Если гораздо более дорогие операции.

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

                return _instance;
            }

А зачем тебе Lock, если ты не собираешься изменять переменную?
Сначала проверяешь на null, чтобы проверить, придётся ли тебе её менять.
Потом поднимаешь lock и проверяешь снова, чтобы гарантировать, что ты один обращаешься.
Для чтения lock не нужен.
Проверка на null - гораздо дешевле, чем lock, по тому перед поднятием блокировки есть смысл проверить на null
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@AndromedaStar
.Net - monkey
lock - это супер дешевая операция. Открытие/закрытие мьютекса - 25 нс. Боюсь, что проверка на null-pointer дороже обходится, но это интересный вопрос. Если вы выясните этот вопрос, что быстрее - будет прикольно узнать.
Но в контексте вашего вопроса траты на lock абсолютно смехотворны, так как затраты будут 50 - 100 нс. Это сравнимо с доступом к RAM.
Да, кстати, я часто сталкивался на собесах, что люди думают, что мьютекс реализован как-то программно на уровне ОС или вообще какая-то магия происходит в виртуальной машине. Но нет, это реализовано прямо на уровне железа, поэтому это настолько быстро.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы