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

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

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

Спасибо.

Ответ 1

Может кто-нибудь объяснить, что такое 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/

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

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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