Знаменитая техника блокировки с двойной проверкой в ​​C # - PullRequest
6 голосов
/ 29 декабря 2011

Я видел в книге mprss эту рекомендацию синглтона (частичный код прилагается):

public static Singleton GetSingleton() 
{
    if (s_value != null) 
        return s_value;
    Monitor.Enter(s_lock); 
    if (s_value == null) 
    {
        Singleton temp = new Singleton();
        Interlocked.Exchange(ref s_value, temp);
    }
    Monitor.Exit(s_lock);
    return s_value;
}

Мы добавляем две строки кода во второй блок оператора if вместо простого написания:

s_value = new Singleton();

это должно обработать ситуацию, когда второй поток входит в метод и находит s_value != null, но не инициализирован.


Мой вопрос, можем ли мы вместо этого написать на втором блоке if:

{    
    Singleton temp = new Singleton();
    s_value = temp;  // instead of Interlocked.Exchange(ref s_value, temp);    
}

Так что теперь функция:

public static Singleton GetSingleton() 
{      
    if (s_value != null)
        return s_value;

    Monitor.Enter(s_lock);   
    if (s_value == null)   
    { 
        Singleton temp = new Singleton();    
        s_value = temp; 
    }   
    Monitor.Exit(s_lock);   
    return s_value; 
} 

Наверное, нет, потому что они им не пользуются.

У кого-нибудь есть предложения?


Возможно, что svalue может содержать неинициализированный? Значение может быть создано сразу после полной инициализации temp (могу я ошибаться). Если я ошибаюсь, можете ли вы привести пример, это неправильно? может ли компилятор создавать другой код?


Ответы [ 2 ]

7 голосов
/ 29 декабря 2011

Я опубликую это не как реальный ответ, а как отступление: если вы используете .NET 4, вам действительно следует рассмотреть шаблон Lazy<T> singleton:

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/05/19/c-system.lazylttgt-and-the-singleton-design-pattern.aspx

public class LazySingleton3
{
   // static holder for instance, need to use lambda to construct since constructor private
   private static readonly Lazy<LazySingleton3> _instance
       = new Lazy<LazySingleton3>(() => new LazySingleton3());

   // private to prevent direct instantiation.
   private LazySingleton3()
   {
   }

   // accessor for instance
   public static LazySingleton3 Instance
   {
       get
       {
           return _instance.Value;
       }
   }

}

Потокобезопасный, легко читаемый и очевидный: что не нравится?

4 голосов
/ 29 декабря 2011

В то время как назначение является атомарным, также требуется, чтобы память «синхронизировалась» между различными ядрами / процессорами (полный забор), или другое ядро, одновременно считывающее значение, могло бы получить устаревшее значение (вне блока синхронизации),Класс Interlocked делает это для всех своих операций.

http://www.albahari.com/threading/part4.aspx

Редактировать: В Википедии есть некоторая полезная информация о проблемах с двойнымсхема блокировки и где / когда использовать барьеры памяти для ее работы.

...