Насколько потокобезопасны неизменяемые объекты? - PullRequest
7 голосов
/ 25 марта 2011

Все говорят, что неизменяемые объекты являются потокобезопасными, но почему это так?

Возьмем следующий сценарий, работающий на многоядерном процессоре:

  • Core1 читает объект в ячейке памяти 0x100 и кэшируется в кеше L1 / L2 ядра 1;
  • GC собирает этот объект в этой ячейке памяти, поскольку он стал пригодным, и 0x100 становится доступнымдля новых объектов:
  • Ядро 2 выделяет (неизменяемый) объект, который расположен по адресу 0x100;
  • Ядро 1 получает ссылку на этот новый объект и считывает его в ячейке памяти 0x100.

В этой ситуации, когда Core 1 запрашивает значение в местоположении 0x100, возможно ли, что оно считывает устаревшие данные из своего кэша L1 / L2?Моя интуиция говорит, что здесь по-прежнему нужен вентиль памяти, чтобы ядро ​​1 считывало правильные данные.

Является ли приведенный выше анализ корректным и требуется ли вентиль памяти, или я что-то упустил?

ОБНОВЛЕНИЕ:

Ситуация, которую я здесь описываю, является более сложной версией того, что происходит каждый раз, когда GC делает сбор.Когда GC собирает, память переупорядочивается.Это означает, что физическое местоположение объекта было изменено, и что L1 / L2 должен быть признан недействительным.Примерно то же самое относится и к приведенному выше примеру.

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

Ответы [ 4 ]

3 голосов
/ 10 августа 2011

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

При исключительно слабой модели памяти такое может быть возможно, но я ожидаю, что любая полезная модель памяти, даже относительно слабая, будет гарантировать, что разыменование неизменяемого объекта будет безопасным, даже если такая безопасность требует заполнения объектов достаточно, чтобы ни одна строка кэша не была разделена между экземплярами объекта (GC почти наверняка лишит законной силы все кэши, когда это будет сделано, но без такого дополнения было бы возможно, что неизменный объект, созданный ядром # 2, мог бы разделить строку кэша с объектом, который ядро № 1 ранее читал). Без этого уровня безопасности для написания надежного кода потребовалось бы столько блокировок и барьеров памяти, что было бы сложно написать многопроцессорный код, который был бы не медленнее однопроцессорного кода.

Популярные модели памяти x86 и x64 обеспечивают гарантию, которую вы ищете, и идут гораздо дальше. Процессоры координируют «владение» строками кэша; если несколько процессоров хотят прочитать одну и ту же строку кэша, они могут сделать это без помех. Когда процессор хочет записать строку кэша, он договаривается о владении другими процессорами. Как только право собственности приобретено, процессор выполнит запись. Другие процессоры не смогут читать или записывать строку кэша, пока процессор, которому принадлежит строка кэша, не откажется от нее. Обратите внимание, что если несколько процессоров хотят одновременно писать одну и ту же строку кэша, они, скорее всего, будут тратить большую часть своего времени на согласование владения строк кэша, а не на выполнение реальной работы, но семантическая корректность будет сохранена.

3 голосов
/ 25 марта 2011

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

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

1 голос
/ 25 марта 2011

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

Ре сценарий, в котором эта ссылка только сейчас в регистрах ... что намного сложнее. Интуитивно я хочу сказать: «Нет, это не проблема», но для того, чтобы доказать это, потребуется подробный взгляд на модель памяти. Но обработка ссылок - это такой общий сценарий, который просто: это должно работать.

1 голос
/ 25 марта 2011

Вам не хватает того, что действительно плохой сборщик мусора позволил бы этому случиться. Ссылка на ядро ​​1 должна была помешать объекту быть GCd.

...