Блокировка с двойной проверкой in.NET
я наткнулся на это статьи обсуждая, почему парадигма блокировки двойной проверки нарушена в Java. Является ли парадигма допустимой для .NET (в частности, C#), если переменные объявлены volatile
?
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; }