Ответ здесь не тривиален. Что именно происходит и что подразумевается, зависит от многих вещей. Для базового понимания когерентности / памяти кэша могут быть полезны мои последние записи в блоге:
Но кроме этого, позвольте мне попытаться ответить на несколько вопросов. Прежде всего, приведенная ниже инструкция очень надеется на то, что поддерживается.
compare_swap( C& expected, C desired,
memory_order success, memory_order failure )
Архитектура не сможет реализовать это в точности так, как вы просили. Когда вы указываете memory_order, вы указываете, как может работать переупорядочение. Чтобы использовать термины Intel, вы будете указывать, какой тип забора вы хотите, есть три из них, полный забор, грузовой забор, и забор магазина. Если вы хотите, чтобы для этой операции была определенная граница, это не означает, что она поддерживается, и я надеюсь, что она всегда будет полностью ограждена.
Компилятор, вероятно, будет использовать инструкцию CMPXCHG
для реализации вызова. Если вы указали что-то отличное от relaxed, оно пометит это как lock
, чтобы указать, что функция должна быть атомарной. Является ли это «свободным от блокировки», очень сильно зависит от того, о чем вы думаете в терминах «блокировки».
С точки зрения синхронизации памяти вы должны понимать, как работает когерентность кэша (мой блог может немного помочь). Новые процессоры используют архитектуру ccNUMA (ранее SMP). По сути, «представление» в памяти никогда не нарушается. Ограждения, используемые в коде, на самом деле не приводят к самопроизвольному сбросу. Если два ядра имеют одно и то же место памяти, кэшированное в строке кэша, одно будет помечено как грязное, а другое перезагрузится при необходимости. Очень простое объяснение очень сложного процесса
Чтобы ответить на ваш последний вопрос, вы всегда должны использовать семантику памяти, которая по логике должна быть правильной. Большинство архитектур не поддерживают все комбинации, которые вы используете в своей программе. Однако во многих случаях вы получите большую оптимизацию, особенно в тех случаях, когда запрашиваемый вами заказ гарантирован без каких-либо ограничений (что довольно часто встречается).
- Ответы на некоторые комментарии:
Вы должны различать, что означает выполнение инструкции записи и запись в ячейку памяти. Это то, что я пытаюсь объяснить в своем блоге. К тому времени, когда «0» фиксируется в 0x100, все ядра видят этот ноль. Запись целых чисел также атомарна, то есть даже без блокировки, когда вы пишете в местоположение, все ядра сразу же получат это значение, если захотят его использовать.
Проблема в том, что для использования значения, которое вы, вероятно, загрузили вначале в регистр, любые изменения местоположения после этого, очевидно, не коснутся регистра. Вот почему нужны мьютексы, несмотря на связную память кеша.
Что касается противоречивых утверждений, в общем случае вы увидите все виды утверждений. Будь они противоречивыми, все сводится к тому, что именно означает «увидеть», «загрузить», «выполнить» в контексте. Если вы записываете «1» в 0x100, означает ли это, что вы выполнили инструкцию записи или ЦП фактически зафиксировал это значение. Разница заключается в переупорядочении. Процессор может задержать запись «1», но вы можете быть уверены, что в тот момент, когда он, наконец, передаст «1», все ядра увидят его. Заборы контролируют этот порядок.