C++, std::atomic, что такое std::memory_order и как их использовать?

Может кто-нибудь объяснить, что такое std::memory_order на простом английском и как их использовать с std::atomic<>?

Я нашел ссылку и несколько примеров здесь, но не понимаю вообще. http://en.cppreference.com/w/cpp/atomic/memory_order

Благодарю.

5 ответов

Решение

Может кто-нибудь объяснить, что такое std::memory_order на простом английском языке,

Лучшее объяснение на "простом английском", которое я нашел для различных упорядочений памяти, - это статья Бартоза Милевского о расслабленной атомике: http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/

И следующий пост: http://bartoszmilewski.com/2008/12/23/the-inscrutable-c-memory-model/

Но обратите внимание, что хотя эти статьи являются хорошим введением, они предшествуют стандарту C++11 и не будут рассказывать вам все, что вам нужно знать, чтобы использовать их безопасно.

и как их использовать с std::atomic<>?

Мой лучший совет для вас здесь: не надо. Расслабленная атомика (возможно) самая хитрая и самая опасная вещь в C++11. Придерживаться std::atomic<T> с упорядочением памяти по умолчанию (последовательная согласованность) до тех пор, пока вы действительно, действительно не будете уверены, что у вас есть проблема с производительностью, которая может быть решена с помощью упрощенного упорядочения памяти.

Во второй статье, упомянутой выше, Бартоз Милевски приходит к следующему выводу:

Я понятия не имел, во что ввязался, пытаясь рассуждать о слабой атомарности C++. Теория, стоящая за ними, настолько сложна, что ее невозможно использовать. Потребовалось три человека (Энтони, Ганс и я) и модификация стандарта, чтобы завершить доказательство относительно простого алгоритма. Представьте, что вы делаете то же самое для очереди без блокировки, основанной на слабой атомарности!

std::memory_order Значения позволяют вам задавать детальные ограничения на порядок памяти, предоставляемый вашими атомарными операциями. Если вы изменяете и обращаетесь к атомарным переменным из нескольких потоков, тогда передача std::memory_order Значения ваших операций позволяют ослабить ограничения для компилятора и процессора в отношении порядка, в котором операции с этими атомарными переменными становятся видимыми для других потоков, и влияния синхронизации, которые эти операции оказывают на неатомарные данные в вашем приложении.

Порядок по умолчанию std::memory_order_seq_cst является наиболее ограниченным и предоставляет "интуитивно понятные" свойства, которые вы можете ожидать: если поток A хранит некоторые данные, а затем устанавливает атомарный флаг, используя std::memory_order_seq_cst затем, если поток B видит, что флаг установлен, он может видеть, что данные, записанные потоком A. Другие значения порядка памяти не обязательно обеспечивают эту гарантию, и поэтому должны использоваться очень осторожно.

Основная предпосылка: не используйте ничего, кроме std::memory_order_seq_cst (по умолчанию), если (а) вы действительно не знаете, что делаете, и можете доказать, что расслабленное использование безопасно во всех случаях, и (б) ваш профилировщик демонстрирует, что структура данных и операции, которые вы намереваетесь использовать смягченные заказы с узким местом.

Моя книга, C++ Concurrency in Action, посвящает целую главу (45 страниц) деталям модели памяти C++, атомарных операций и std::memory_order ограничения, а также еще одну главу (44 страницы), посвященную использованию атомарных операций для синхронизации в структурах данных без блокировок, и последствиям смягченных ограничений порядка.

Мои записи в блоге об алгоритме Деккера и алгоритме взаимного исключения Петерсона демонстрируют некоторые проблемы.

Нет. Объяснение "простым английским" занимает 32 страницы и его можно найти здесь.

Если вы не хотите читать это, вы можете забыть об упорядочивании памяти, потому что на странице, на которую вы ссылаетесь, указано, что по умолчанию используется последовательное согласование, то есть "всегда делайте вменяемое".

Чтобы использовать любой другой параметр, вы действительно должны прочитать и понять вышеупомянутый документ и примеры в нем.

Вкратце, ваш компилятор и процессор могут выполнять инструкции в порядке, отличном от того, как вы их написали. Для однопоточности это не проблема, так как будет выглядеть правильно. Для нескольких потоков на нескольких процессорах это становится проблемой. Упорядочение памяти в C++ ограничивает возможности вашего компилятора / процессора и устраняет такие проблемы.

Например, если вы посмотрите мою статью о двойной проверке блокировки, вы увидите, как упорядочение мешает с этим шаблоном - в нем упоминается, что для исправления может использоваться порядок атомарной памяти.

Что касается самого переупорядочения, вы также можете рассмотреть переупорядочение процессора - опять же, компилятор также может выполнять переупорядочения.

Помните, что любые документы на эту тему (включая мою) предлагают говорить о теоретических сценариях. Наиболее распространенные процессоры, такие как x86, имеют очень строгие гарантии упорядочения, так что многие явные упорядочения просто не нужны. Таким образом, даже если вы не используете правильную атомарность C++11, ваш код, скорее всего, все равно будет работать.

Как упомянул zvrba, тема на самом деле довольно подробная. Документация по ядру Linux на барьерах памяти также содержит много подробной информации.

В GCC Wiki есть простой английский.;)

http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync

Другие вопросы по тегам