Двойная проверка блокировки в одноточечном шаблоне

это может быть основной вопрос

чтобы иметь singleton в многопоточной среде, мы можем использовать блокировку. См. Фрагмент кода. Но почему нам нужна двойная проверка блокировки в одноэлементном шаблоне? И еще, что означает блокировка с двойной проверкой?

class singleton
{
    private static singleton instance = null;
    private static singleton() { }

    private static object objectlock = new object();

    public static singleton Instance
    {
        get
        {

            lock (objectlock) //single - check lock
            {
                if (instance == null)
                {
                    instance = new singleton();
                }

                return instance;
            }
        }

    }
}

Ответы

Ответ 1

Jon Skeet подробно объясняет это.

Замки дороги.
Если объект уже существует, нет смысла снимать блокировку.
Таким образом, у вас есть первая проверка за пределами блокировки.

Однако, даже если объект не существовал до того, как вы посмотрели, другой поток, возможно, создал его между условием if и оператором lock.
Поэтому вам нужно снова проверить внутри замка.

Однако лучший способ написать singleton - использовать конструктор static:

public sealed class Singleton
{
    private Singleton()
    {
    }

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

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

        internal static readonly Singleton instance = new Singleton();
    }
} 

Ответ 2

И с .Net 4.x и новее вы должны по возможности относиться к классу Lazy, поскольку этот шаблон используется с параметром Initialization And Publication. (примечание: обратное также доступно, где создание не является потокобезопасным, но публикация экземпляра осуществляется через вариант публикации)

Ответ 3

"Лучший" способ, который я знаю, это:

public class MySingleton {
    // object for synchronization
    private static readonly object syncRoot = new object();
    // the singleton instance
    private static MySingleton @default;

    public static MySingleton Default {
        get {
            // geting singleton instance without locking
            var result = @default;
            // if result is NOT null, no additional action is required
            if ( object.ReferenceEquals(result, null) ){
                // lock the synchronization object
                lock(syncRoot) {
                    // geting singleton instanc in lock - because
                    // the value of @default field could be changed
                    result = @default;

                    // checking for NULL
                    if ( object.ReferenceEquals(result, null) ) {
                        // if result is NULL, create new singleton instance
                        result = new MySingleton();
                        // set the default instance
                        @default = result;
                    }
                }
            }

            // return singleton instance
            return result;
        }
    }
}

Ответ 4

Если вы создаете объект в инициализаторе поля, вам не нужна блокировка:

class singleton
{
    private static singleton instance = new singleton();
    private static singleton() { }

    public static singleton Instance
    {
        get { return instance; }
    }
}

Также - имейте в виду, что блокировка управляет только созданием объекта, объект все равно должен быть потокобезопасным, если вы используете его в нескольких потоках.

Ответ 5

Многопоточный синглтон: лучший подход к использованию двойной проверки блокировки

public sealed class Singleton
{
   private static volatile Singleton _instance;
   private static readonly object InstanceLoker= new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (_instance == null) 
         {
            lock (InstanceLoker) 
            {
               if (_instance == null) 
                  _instance = new Singleton();
            }
         }

         return _instance;
      }
   }
}

Ответ 6

Когда мы пытаемся выполнить метод одиночного класса с использованием параллельных библиотек. Он не распознает поведение singleton из-за многопоточности, выполняемой в TPL, что приводит к сбою концепции Singleton Pattern. Для преодоления этой проблемы существует концепция блокировки объекта, чтобы в то время к нему мог обращаться только один поток. Но это неэффективный подход, потому что участие проверки блокировки создает ненужные часы для объекта. Чтобы избежать этого, мы используем "Проверка двойной блокировки"

введите описание изображения здесь