Оставляя в стороне уловки, выполняемые компилятором (даже те, которые разрешены языковыми стандартами), я полагаю, вы спрашиваете, как микроархитектура может вести себя в таком сценарии. Имейте в виду, что код, скорее всего, расширится до цикла ожидания занятости cmp [x] + jz
или чего-то подобного, скрывающего внутри себя нагрузку. Это означает, что [x], скорее всего, будет находиться в кэше основного потока 1.
В какой-то момент придет поток 2 и выполнит сохранение. Если он находится на другом ядре, строка сначала будет полностью аннулирована из первого ядра. Если это 2 потока, запущенные на одном физическом ядре, хранилище немедленно повлияет на все хронологически более молодые нагрузки.
Теперь, наиболее вероятно, что на современной вышедшей из строя машине все нагрузки в конвейере в этот момент будут разными итерациями одного и того же первого цикла (поскольку любой предиктор ветвления сталкивается с таким количеством повторяющихся "взятых" разрешений вероятно, будет предполагать, что ветвь будет продолжать выполняться, пока не будет доказана ошибка), поэтому произойдет то, что первая загрузка, которая встретит новое значение, измененное другим потоком, приведет к тому, что соответствующая ветвь просто очистит всю трубу от всех младших операций , без возможности выполнения второго цикла.
Однако возможно, что по какой-то причине вы попали во второй цикл (допустим, предсказатель выдает невыполненный прогноз как раз в нужный момент, когда проверка условия цикла увидела новое значение) - в этом случае вопрос сводится к следующему. к этому сценарию:
Time -->
----------------------------------------------------------------
thread 1
cmp [x],0 execute
je ... execute (not taken)
...
cmp [x],0 execute
jne ... execute (not taken)
Can_We_Get_Here:
...
thread2
store [x],1 execute
Другими словами, учитывая, что большинство современных процессоров могут выполнять инструкции не по порядку, может ли более молодая нагрузка оцениваться перед более старой по тому же адресу, что позволяет хранилищу (из другого потока) изменять значение, чтобы оно могло непоследовательно наблюдаться нагрузки.
Я предполагаю, что указанная выше временная шкала вполне возможна, учитывая природу механизмов исполнения вне очереди сегодня, поскольку они просто арбитраж и выполняют любую операцию, которая готова. Однако в большинстве реализаций x86 есть меры предосторожности для защиты от такого сценария, поскольку правила упорядочивания памяти строго скажем -
8.2.3.2 Neither Loads Nor Stores Are Reordered with Like Operations
Такие механизмы могут обнаружить этот сценарий и промыть машину, чтобы предотвратить появление устаревших / неправильных значений. Итак, ответ - нет, это не должно быть возможным, если, конечно, программное обеспечение или компилятор не изменят характер кода, чтобы аппаратное обеспечение не могло заметить взаимосвязь. С другой стороны, правила упорядочивания памяти иногда нестабильны, и я не уверен, что все производители x86 придерживаются одной и той же формулировки, но это довольно фундаментальный пример согласованности, поэтому я был бы очень удивлен, если бы один из них пропустил это.
person
Leeor
schedule
04.10.2014
print(x); print(x);
вold = x; print(x); print(old);
. Так что на большинстве языков ответ положительный. - person usr   schedule 05.10.2014volatile
операции с разделяемыми объектами, и они должны выполняться точно в соответствии с ABI; это часть наблюдаемого поведения. Таким образом, у нас есть в точности семантика, обеспечиваемая ЦП, когда читаются и записываютсяint
объектов, соответствующих ABI (выровненных соответственно). Нет UB, только поведение ABI и CPU. - person curiousguy   schedule 28.05.2019