@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;
            }
        }
    }
  • Вопрос задан
  • 231 просмотр
Решения вопроса 1
vabka
@vabka Куратор тега .NET
Токсичный шарпист
зачем делать 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.
Да, кстати, я часто сталкивался на собесах, что люди думают, что мьютекс реализован как-то программно на уровне ОС или вообще какая-то магия происходит в виртуальной машине. Но нет, это реализовано прямо на уровне железа, поэтому это настолько быстро.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы