Блокировка с двойной проверкой in.NET


я наткнулся на это статьи обсуждая, почему парадигма блокировки двойной проверки нарушена в Java. Является ли парадигма допустимой для .NET (в частности, C#), если переменные объявлены volatile?

8 52

8 ответов:

реализует паттерн Singleton в C# переговоры об этой проблеме в третьей версии.

Он говорит:

создание переменной экземпляра volatile может заставить его работать, как и явные вызовы барьера памяти, хотя в последнем случае даже эксперты не могут точно определить, какие барьеры требуются. Я стараюсь избегать ситуаций, когда эксперты не согласны с тем, что правильно и что неправильно!

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

двойная проверка блокировки теперь работает в Java, а также C# (модель памяти Java изменилась, и это один из эффектов). Однако, вы должны получить его ровно право. Если вы испортите вещи даже немного, вы можете в конечном итоге потерять безопасность потока.

Как и другие ответы заявили, если вы реализуете синглтон шаблон есть гораздо лучшие способы сделать это. Лично, если я нахожусь в ситуации, когда мне приходится выбирать между двойной проверкой блокировка и" блокировка каждый раз " код я бы пошел на блокировку каждый раз, пока я не получил реальных доказательств того, что это вызывает узкое место. Когда дело доходит до нарезания резьбы, простой и очевидно правильный шаблон стоит много.

.NET 4.0 имеет новый тип:Lazy<T> Это снимает любую озабоченность по поводу получения шаблона неправильно. Это часть новой параллельной библиотеки задач.

см. MSDN Parallel Computing Dev Center:http://msdn.microsoft.com/en-us/concurrency/default.aspx

кстати, есть backport (я считаю, что он не поддерживается) для .NET 3.5 SP1 доступен здесь.

Примечание, чем в Java (и, скорее всего, в .Net также), дважды проверенная блокировка для инициализации синглтона совершенно не нужна, а также сломана. Поскольку классы не инициализируются до их первого использования, желаемая ленивая инициализация уже достигается этим;

private static Singleton instance = new Singleton();

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

я получил двойную проверку блокировки для работы с помощью логического (т. е. с помощью примитива, чтобы избежать ленивой инициализации):

синглтон с помощью boolean не работает. Порядок операций, как видно между различными потоками, не гарантируется, Если вы не пройдете через барьер памяти. Другими словами, Как видно из второго потока, created = true может быть выполнена до instance= new Singleton();

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

Если кто-то может сказать мне, если этот код страдает от проблемы описанные в статье Кэмерона, пожалуйста.

public sealed class Singleton {
    static Singleton instance = null;
    static readonly object padlock = new object();

    Singleton() {
    }

    public static Singleton Instance {
        get {
            if (instance != null) {
                return instance;
            }

            lock (padlock) {
                if (instance != null) {
                    return instance;
                }

                tempInstance = new Singleton();

                // initialize the object with data

                instance = tempInstance;
            }
            return instance;
        }
    }
}

Я не совсем понимаю, почему существует куча шаблонов реализации при двойной проверке блокировки (по-видимому, для работы с идиосинкразиями компилятора на разных языках). Статья Википедии на эту тему показывает наивный метод и возможные способы решения проблемы, но ни один из них не так прост, как это (в C#):

public class Foo
{
  static Foo _singleton = null;
  static object _singletonLock = new object();

  public static Foo Singleton
  {
    get
    {
      if ( _singleton == null )
        lock ( _singletonLock )
          if ( _singleton == null )
          {
            Foo foo = new Foo();

            // Do possibly lengthy initialization,
            // but make sure the initialization
            // chain doesn't invoke Foo.Singleton.
            foo.Initialize();

            // _singleton remains null until
            // object construction is done.
            _singleton = foo;
          }
      return _singleton;
    }
  }

в Java вы бы использовали synchronized () вместо lock (), но это в основном та же идея. Если есть возможная несогласованность, когда одноэлементное поле назначается, тогда почему бы просто не использовать переменную локальной области, а затем назначить одноэлементное поле в последний возможный момент перед выходом из критической секции? Я что-то упустил?

есть аргумент @michael-borgwardt, что в C# и Java статическое поле инициализируется только один раз при первом использовании, но это поведение зависит от языка. И я часто использовал этот шаблон для ленивой инициализации свойства коллекции (например пользователь.Проведение сессий.)

Я получил двойную проверку блокировки для работы с помощью логического (т. е. с помощью примитива, чтобы избежать ленивой инициализации):

private static Singleton instance;
private static boolean created;
public static Singleton getInstance() {
    if (!created) {
        synchronized (Singleton.class) {
            if (!created) {
                instance = new Singleton();
                created = true;
            }
        }
    }
    return instance;
}