Стоит ли ставить магазин-магазин-барьер в конструкторы?
Вот пример. Предполагать global_f = f = r = 0
первоначально.
Один поток A создает объект, присваивает его полю и присваивает его глобальной переменной:
class Foo {
int x;
void Foo(int x) {
this.x = x;
}
}
f = new Foo(42);
global_f = f;
Другой поток B получает ссылку из глобальной переменной и затем читает поле.
r = global_f.x;
Предполагая выполнения, когда поток B читает ссылку на объект из потока B, какое значение он может прочитать из X
в r
? Всегда 42 или нет?
Меня интересует поведение C ++ а также Джава. Насколько я понимаю модели памяти, r
не гарантируется быть 42.
Чтобы гарантировать правильную инициализацию полей объекта, мы могли бы поставить store-store-барьеры в конце конструкторов вообще. Это кажется подводным камнем в C ++ и Java. По крайней мере, для Java все хорошо для конечных полей, не так ли? На практике это не так критично, потому что по крайней мере на x86 и AMD64 барьер между магазином и магазином является NOP. Однако на других архитектурах, таких как ARM или POWER, это не так.
В Java нет гарантии, что r
будет 42
если вы не объявите x
поле как final
,
Чтобы гарантировать правильную инициализацию полей объекта, мы могли бы поставить store-store-барьеры в конце конструкторов вообще. Это кажется подводным камнем в C ++ и Java.
Это комментарий, а не вопрос. Однако противоположный аргумент заключается в том, что установка неявного барьера в конце всех конструкторов приводит к ненужному снижению производительности в различных случаях; например
f
не опубликованоx
охраняется синхронизированным методом.Модели памяти Java и C ++ представляют собой компромисс между простотой и соображениями производительности. Если вы слишком далеко зайдете в сторону простоты (то есть, разработав так называемые ловушки), многопоточный код не даст вам такого ускорения, которого хотят / нуждают люди.
Других решений пока нет …