Потребность в энергозависимом модификаторе в проверенной дважды.NET привязки

В нескольких текстах говорится, что, когда реализация перепроверила привязывающуюся.NET поле, Вы соединяетесь, должно применить энергозависимый модификатор. Но почему точно? Рассмотрение следующего примера:

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}

почему "не блокирует (syncRoot)", выполняют необходимую непротиворечивость памяти? Разве это не верно, что после оператора "блокировки" и чтение и запись были бы энергозависимы и таким образом, необходимая непротиворечивость будет выполнена?

80
задан Konstantin 27 December 2009 в 00:13
поделиться

6 ответов

Энергонезависимость не нужна. Что-то вроде**

volatile используется для создания барьера памяти* между считыванием и записью на переменную.
lock, при использовании, приводит к созданию барьеров памяти вокруг блока внутри блокировки lock, в дополнение к ограничению доступа к блоку одним потоком.
. Барьеры памяти делают так, чтобы каждый поток читал самое текущее значение переменной (а не локальное значение, кэшированное в каком-нибудь регистре), и чтобы компилятор не переупорядочивал операторы. Использовать volatile не нужно**, так как у вас уже есть блокировка.

Джозеф Альбахари объясняет эти вещи намного лучше, чем я когда-либо мог.

И обязательно прочитайте руководство Джона Скита по реализации синглтона в C#


update:
:
. *volatile приводит к тому, что показания переменной являются VolatileReads, а запись - VolatileWrite s, которые на x86 и x64 на CLR реализованы с помощью MemoryBarrier. На других системах они могут быть более мелкими.

**мой ответ верен только в том случае, если вы используете CLR на процессорах x86 и x64. Это может быть верно для других моделей памяти, таких как Mono (и других реализаций), Itanium64 и будущих аппаратных средств. Именно на это ссылается Джон в своей статье в "getchas" для двойной проверки блокировки.

Для корректной работы кода в ситуации со слабой моделью памяти может понадобиться {маркировка переменной как volatile, чтение с помощью Thread.VolatileRead, или вставка вызова на Thread.MemoryBarrier}.

Насколько я понял, на CLR (даже на IA64) записи никогда не переупорядочиваются (на записи всегда присутствует семантика выпуска). Однако на IA64 чтение может быть переупорядочено, чтобы прийти до записи, если только они не помечены как изменчивые. К сожалению, у меня нет доступа к аппаратной части IA64, с которой можно было бы поиграть, так что все, что я скажу по этому поводу, было бы спекуляцией.

Я также нашел эти статьи полезными:
http://www.codeproject.com/KB/tips/MemoryBarrier. aspx
vance morrison's article (все ссылки на это, в ней говорится о блокировке с двойной проверкой)
chris brumme article (все ссылки на это)
Joe Duffy: Broken Variants of Double Checked Locking

luis abreu's series on multithreading тоже дает хороший обзор концепций
http://msmvps. com/blogs/luisabreu/archive/2009/06/29/multithreading-load-and-store-reordering.aspx
http://msmvps.com/blogs/luisabreu/archive/2009/07/03/multithreading-introducing-memory-fences.aspx

59
ответ дан 24 November 2019 в 10:00
поделиться

достаточно блокировки lock. Сама спецификация языка MS (3.0) упоминает этот точный сценарий в §8.12, без упоминания volatile:

Лучший подход - синхронизировать доступ к статическим данным путем блокировки частный статический объект. Например:

класс Cache
{
 private static object synchronizationObject = new object();
 public static void Add(object x) {
 блокировка (Cache.synchronizationObject) {
 ...
 }
 }
 публичная статическая пустота Удалить(объект x) {
 блокировка (Cache.synchronizationObject) {
 ...
 }
 }
}
2
ответ дан 24 November 2019 в 10:00
поделиться
-

Я думаю, что нашел то, что искал. Подробности в этой статье - http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S10.

Подводя итог - в .NET волатильный модификатор в этой ситуации действительно не нужен. Однако в более слабых моделях памяти записи, сделанные в конструкторе лениво инициируемого объекта, могут быть отложены после записи в поле, поэтому другие потоки могут прочитать поврежденный ненулевой экземпляр в первом if операторе.

.
2
ответ дан 24 November 2019 в 10:00
поделиться

Не думаю, что кто-то действительно ответил на вопрос , так что попробую.

Волатильность и первая , если (экземпляр == нуль) не "нужны". Блокировка сделает этот код потокобезопасным.

Итак, вопрос в том, зачем добавлять первую , если (экземпляр == null)?

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

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

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

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

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

Теперь, это действительно полезно?

Ну, я бы сказал "в большинстве случаев, нет".

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

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

Итак, я подозреваю, что блокировка с двойной проверкой - это механизм, который имеет реальное место в очень специфических случаях, критичных с точки зрения производительности, и тогда все закричали на "это правильный способ сделать это", не задумываясь о том, что он делает и будет ли он действительно необходим в том случае, если они используют его для этого.

.
7
ответ дан 24 November 2019 в 10:00
поделиться

Довольно неплохой пост об использовании volatile с двойной проверкой блокировки:

http://tech.puredanger.com/2007/06/15/double-checked-locking/

В Java, если целью является защита переменной, то нет необходимости блокировать ее, если она помечена как volatile

.
-2
ответ дан 24 November 2019 в 10:00
поделиться

AFAIK (и - принимай это с осторожностью, я не делаю много одновременных вещей) нет. Блокировка просто дает синхронизацию между несколькими соперниками (потоками). С другой стороны

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

Смотрите http://msdn.microsoft.com/en-us/library/ms998558.aspx и обратите внимание на следующую цитату:

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

Описание нестабильной переменной: http://msdn.microsoft.com/en-us/library/x13ttww7%28VS.71. %29.aspx

3
ответ дан 24 November 2019 в 10:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: