По теме: мой ответ на Что формально гарантирует, что неатомарные переменные не могут видеть неожиданные значения и создавать гонку данных, как теоретически может атомарное расслабление? более подробно объясняет, что формальные правила C ++ ослабили атомную память модель не исключает из воздуха значений. Но они действительно исключают их в примечании. Это проблема только для формальной проверки программ, использующих mo_relaxed
, а не для реальных реализаций. Даже неатомарные переменные защищены от этого, если вы избегаете неопределенного поведения (которое вы не было в коде в этом вопросе).
У вас есть гонка данных Undefined Behavior на x
и y
, потому что они не atomic
переменные, поэтому в стандарте C ++ 11 абсолютно ничего не говорится о том, что может происходить.
Было бы уместно взглянуть на это для более старых языковых стандартов без формальной модели памяти, где люди все равно выполняли потоки, используя volatile
или простой int
и барьеры compiler + asm, где поведение могло зависеть от компиляторов, работающих так, как вы ожидаете в подобном случае. Но, к счастью, старые плохие времена работы над текущими реализациями потоковой передачи остались позади.
Барьеры здесь бесполезны, ведь нечем создать синхронизацию; как объясняет @davmac, ничто не требует выстраивания барьеров в глобальном порядке операций. Подумайте о барьере как об операции, которая заставляет текущий поток ждать, пока некоторые или все его предыдущие операции станут глобально видимыми; барьеры не взаимодействуют напрямую с другими цепочками.
Неопределенные значения - это то, что может произойти в результате такого неопределенного поведения; компилятору разрешено делать программное прогнозирование значений для неатомарных переменных и изобретать записи в объекты, которые определенно будут записаны в любом случае. Если бы было хранилище релизов или хранилище с ослабленным режимом + барьер, компилятору могло бы быть запрещено изобретать записи перед ним, потому что это могло создавать
В общем, с точки зрения юриста по языку C ++ 11, вы ничего не можете сделать, чтобы сделать вашу программу безопасной (кроме мьютекса или ручной блокировки с помощью атомики, чтобы предотвратить чтение одним потоком x
, в то время как другой его пишет).
Расслабленной атомики достаточно, чтобы компилятор не изобретал записи без каких-либо других затрат.
За исключением, возможно, победы над автоматической векторизацией и прочим, если вы рассчитывали на агрессивную оптимизацию других способов использования этой переменной.
atomic_int x = 0, y = 0
r1 = x.load(mo_relaxed) | r2 = y.load(mo_relaxed)
y.store(r1, mo_relaxed) | x.store(r2, mo_relaxed)
Прогнозирование значения может спекулятивно получить будущее значение для r2
в конвейер до того, как поток 2 увидит это значение из y
, но на самом деле оно не может стать видимым для других потоков, пока программное обеспечение или оборудование не узнают наверняка, что прогноз был правильным. (Это было бы изобретением записи).
например поток 2 может компилироваться как
r2 = y.load(mo_relaxed);
if (r2 == 42) { // control dependency, not a data dependency
x.store(42, mo_relaxed);
} else {
x.store(r2, mo_relaxed);
}
Но, как я уже сказал, x = 42;
не может стать видимым для других потоков, пока он не является неспекулятивным (аппаратное или программное обеспечение), поэтому прогнозирование значений не может изобретать значения, которые могут видеть другие потоки. Стандарт C ++ 11 гарантирует, что атомики
Я не знаю / не могу придумать какой-либо механизм, с помощью которого хранилище 42
могло бы быть действительно видимым для других потоков до того, как y.load
увидит фактическое 42. (т.е. переупорядочение LoadStore загрузки с более поздним зависимым хранилищем). Однако я не думаю, что стандарт C ++ формально гарантирует это. Может быть, действительно агрессивная межпоточная оптимизация, если компилятор сможет доказать, что r2
всегда будет 42 в некоторых случаях, и удалит даже зависимость управления?
Чтобы заблокировать нарушения причинно-следственной связи, определенно будет достаточно накопления загрузки или выпуска. Это не совсем mo_consume
, потому что r2
используется как значение, а не указатель.
person
Peter Cordes
schedule
08.07.2018