Ответ 1
GCC Wiki дает очень полное и простое для понимания объяснение с примерами кода.
(отредактированный отрывок и выделение добавлено)
ВАЖНЫЙ:
Перечитав приведенную ниже цитату, скопированную из вики GCC, в процессе добавления моей собственной формулировки к ответу, я заметил, что цитата на самом деле неверна. Они приобретают и потребляют совершенно неправильно. Операция деблокирования предоставляет только гарантию упорядочения зависимых данных, тогда как операция получения релиза обеспечивает эту гарантию независимо от того, зависят данные от атомарного значения или нет.
Первая модель является "последовательно последовательной". Это режим по умолчанию, используемый, когда он не указан, и он является наиболее ограничительным. Это также может быть явно указано через
memory_order_seq_cst
. Он обеспечивает те же ограничения и ограничения для перемещения нагрузок, с которыми последовательные программисты по своей природе знакомы, за исключением того, что он применяется ко всем потокам.
[...]
С практической точки зрения это сводится ко всем атомным операциям, выступающим в качестве барьеров для оптимизации. Это нормально, чтобы переупорядочить вещи между атомарными операциями, но не через операцию. Локальное содержимое потока также не затронуто, так как нет никакой видимости другим потокам. [...] Этот режим также обеспечивает согласованность всех потоков.Противоположный подход -
memory_order_relaxed
. Эта модель обеспечивает гораздо меньшую синхронизацию, устраняя ограничения, возникающие до того. У этих типов атомарных операций также могут быть различные оптимизации, такие как удаление мертвых хранилищ и их объединение. [...] Без каких-либо ребер, предшествующих событию, ни один поток не может рассчитывать на конкретный порядок в другом потоке.
Расслабленный режим чаще всего используется, когда программист просто хочет, чтобы переменная была атомарной по природе, а не использовала ее для синхронизации потоков для других данных общей памяти.Третий режим (
memory_order_acquire
/memory_order_release
) представляет собой гибрид между двумя другими. Режим получения/выпуска аналогичен последовательно согласованному режиму, за исключением того, что он применяет только отношение "до и к" для зависимых переменных. Это позволяет ослабить синхронизацию, требуемую между независимыми чтениями независимых записей.
memory_order_consume
- это еще одно тонкое уточнение в модели памяти освобождения/приобретения, которое слегка ослабляет требования, удаляя случай перед упорядочиванием по независимым разделяемым переменным.
[...]
Реальная разница сводится к тому, в каком состоянии аппаратное обеспечение должно быть сброшено для синхронизации. Поскольку операция потребления может, следовательно, выполняться быстрее, тот, кто знает, что делает, может использовать ее для приложений, критичных для производительности.
Вот моя собственная попытка более мирского объяснения:
Другой подход к этому - взглянуть на проблему с точки зрения переупорядочения операций чтения и записи, как атомарных, так и обычных:
Все атомарные операции гарантированно должны быть атомарными внутри себя (комбинация двух атомарных операций не является атомарной в целом!) И быть видимой в общем порядке, в котором они появляются на временной шкале потока выполнения. Это означает, что никакие атомарные операции ни при каких обстоятельствах не могут быть переупорядочены, но вполне могут быть и другие операции с памятью. Компиляторы (и процессоры) обычно выполняют такое переупорядочение как оптимизацию.
Это также означает, что компилятор должен использовать любые необходимые инструкции, чтобы гарантировать, что выполняемая в любое время атомарная операция будет видеть результаты каждой и всех других атомарных операций, возможно, на другом ядре процессора (но не обязательно других операций), которые были выполнены до,
Теперь расслабленный - это просто минимум. Кроме того, он ничего не делает и не предоставляет никаких других гарантий. Это самая дешевая операция. Для операций чтения-изменения-записи в строго упорядоченных процессорных архитектурах (например, x86/amd64) это сводится к обычному обычному ходу.
Последовательная последовательная операция является полной противоположностью, она обеспечивает строгое упорядочение не только для атомарных операций, но и для других операций с памятью, которые происходят до или после. Никто не может пересечь барьер, наложенный атомной операцией. На практике это означает потерю возможностей оптимизации, и, возможно, придется вставить инструкции по ограничению. Это самая дорогая модель.
Операция освобождения предотвращает переупорядочение обычных нагрузок и накопителей после атомарной операции, тогда как операция получения предотвращает переупорядочение обычных нагрузок и накопителей перед атомарной операцией. Все остальное еще можно перемещать.
Сочетание предотвращения перемещения хранилищ после и загрузки перед соответствующей атомарной операцией гарантирует, что то, что видит поток-получатель, будет согласованным, при этом будет потеряно лишь небольшое количество возможностей оптимизации.
Можно думать об этом как о чем-то вроде несуществующей блокировки, которая освобождается (писателем) и приобретается (читателем). За исключением... нет блокировки.
На практике освобождение/приобретение обычно означает, что компилятору не нужно использовать какие-либо особо дорогие специальные инструкции, но он не может свободно переупорядочивать загрузки и сохранять данные по своему вкусу, что может упустить некоторые (небольшие) возможности оптимизации.
Наконец, потребление - это та же операция, что и приобретение, за исключением того, что гарантии упорядочения применяются только к зависимым данным. К зависимым данным относятся, например, данные, на которые указывает атомно-модифицированный указатель.
Можно предположить, что это может обеспечить пару возможностей оптимизации, которые отсутствуют в операциях получения (так как на меньшее количество данных распространяются ограничения), однако это происходит за счет более сложного и более подверженного ошибкам кода и нетривиальной задачи. правильных цепочек зависимостей.
В настоящее время не рекомендуется использовать порядок потребления во время пересмотра спецификации.