Подтвердить что ты не робот

Что означает каждая память?

Я прочитал главу, и мне это не понравилось. Я все еще не понимаю, каковы различия между каждым порядком памяти. Это мои текущие предположения, которые я понял после прочтения гораздо более простых http://en.cppreference.com/w/cpp/atomic/memory_order

Ниже указано неверно, поэтому не пытайтесь учиться на нем

  • memory_order_relaxed: не синхронизируется, но не игнорируется, когда заказ выполняется из другого режима в другой атомной var
  • memory_order_consume: Синхронизация чтения этой атомной переменной, но она не синхронизирует расслабленные вары, написанные до этого. Однако, если поток использует var X при изменении Y (и освобождает его). Другие потоки, потребляющие Y, также увидят X? Я не знаю, означает ли это, что эта нить выталкивает изменения x (и, очевидно, y)
  • memory_order_acquire: Синхронизация чтения этой атомной переменной И гарантирует, что расслабленные вары, написанные до этого, также синхронизируются. (означает ли это, что все атомные переменные во всех потоках синхронизируются?)
  • memory_order_release: помещает атомный магазин в другие потоки (но только если они читают var с потреблением/приобретением)
  • memory_order_acq_rel: для операций чтения/записи. Получает ли приобретение, поэтому вы не изменяете старое значение и не выпускаете изменения.
  • memory_order_seq_cst: то же самое, что и получение релиза, за исключением того, что оно заставляет обновления отображаться в других потоках (если a сохранить с расслабленным в другом потоке.Я храню b с seq_cst. 3-й поток, читающий a с relax увидит изменения вместе с b и любой другой атомной переменной?).

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

4b9b3361

Ответ 1

GCC Wiki дает очень полное и простое для понимания объяснение с примерами кода.

(отредактированный отрывок и выделение добавлено)

ВАЖНЫЙ:

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

Первая модель является "последовательно последовательной". Это режим по умолчанию, используемый, когда он не указан, и он является наиболее ограничительным. Это также может быть явно указано через memory_order_seq_cst. Он обеспечивает те же ограничения и ограничения для перемещения нагрузок, с которыми последовательные программисты по своей природе знакомы, за исключением того, что он применяется ко всем потокам.
[...]
С практической точки зрения это сводится ко всем атомным операциям, выступающим в качестве барьеров для оптимизации. Это нормально, чтобы переупорядочить вещи между атомарными операциями, но не через операцию. Локальное содержимое потока также не затронуто, так как нет никакой видимости другим потокам. [...] Этот режим также обеспечивает согласованность всех потоков.

Противоположный подход - memory_order_relaxed. Эта модель обеспечивает гораздо меньшую синхронизацию, устраняя ограничения, возникающие до того. У этих типов атомарных операций также могут быть различные оптимизации, такие как удаление мертвых хранилищ и их объединение. [...] Без каких-либо ребер, предшествующих событию, ни один поток не может рассчитывать на конкретный порядок в другом потоке.
Расслабленный режим чаще всего используется, когда программист просто хочет, чтобы переменная была атомарной по природе, а не использовала ее для синхронизации потоков для других данных общей памяти.

Третий режим (memory_order_acquire/memory_order_release) представляет собой гибрид между двумя другими. Режим получения/выпуска аналогичен последовательно согласованному режиму, за исключением того, что он применяет только отношение "до и к" для зависимых переменных. Это позволяет ослабить синхронизацию, требуемую между независимыми чтениями независимых записей.

memory_order_consume - это еще одно тонкое уточнение в модели памяти освобождения/приобретения, которое слегка ослабляет требования, удаляя случай перед упорядочиванием по независимым разделяемым переменным.
[...]
Реальная разница сводится к тому, в каком состоянии аппаратное обеспечение должно быть сброшено для синхронизации. Поскольку операция потребления может, следовательно, выполняться быстрее, тот, кто знает, что делает, может использовать ее для приложений, критичных для производительности.

Вот моя собственная попытка более мирского объяснения:

Другой подход к этому - взглянуть на проблему с точки зрения переупорядочения операций чтения и записи, как атомарных, так и обычных:

Все атомарные операции гарантированно должны быть атомарными внутри себя (комбинация двух атомарных операций не является атомарной в целом!) И быть видимой в общем порядке, в котором они появляются на временной шкале потока выполнения. Это означает, что никакие атомарные операции ни при каких обстоятельствах не могут быть переупорядочены, но вполне могут быть и другие операции с памятью. Компиляторы (и процессоры) обычно выполняют такое переупорядочение как оптимизацию.
Это также означает, что компилятор должен использовать любые необходимые инструкции, чтобы гарантировать, что выполняемая в любое время атомарная операция будет видеть результаты каждой и всех других атомарных операций, возможно, на другом ядре процессора (но не обязательно других операций), которые были выполнены до,

Теперь расслабленный - это просто минимум. Кроме того, он ничего не делает и не предоставляет никаких других гарантий. Это самая дешевая операция. Для операций чтения-изменения-записи в строго упорядоченных процессорных архитектурах (например, x86/amd64) это сводится к обычному обычному ходу.

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

Операция освобождения предотвращает переупорядочение обычных нагрузок и накопителей после атомарной операции, тогда как операция получения предотвращает переупорядочение обычных нагрузок и накопителей перед атомарной операцией. Все остальное еще можно перемещать.
Сочетание предотвращения перемещения хранилищ после и загрузки перед соответствующей атомарной операцией гарантирует, что то, что видит поток-получатель, будет согласованным, при этом будет потеряно лишь небольшое количество возможностей оптимизации.
Можно думать об этом как о чем-то вроде несуществующей блокировки, которая освобождается (писателем) и приобретается (читателем). За исключением... нет блокировки.

На практике освобождение/приобретение обычно означает, что компилятору не нужно использовать какие-либо особо дорогие специальные инструкции, но он не может свободно переупорядочивать загрузки и сохранять данные по своему вкусу, что может упустить некоторые (небольшие) возможности оптимизации.

Наконец, потребление - это та же операция, что и приобретение, за исключением того, что гарантии упорядочения применяются только к зависимым данным. К зависимым данным относятся, например, данные, на которые указывает атомно-модифицированный указатель.
Можно предположить, что это может обеспечить пару возможностей оптимизации, которые отсутствуют в операциях получения (так как на меньшее количество данных распространяются ограничения), однако это происходит за счет более сложного и более подверженного ошибкам кода и нетривиальной задачи. правильных цепочек зависимостей.

В настоящее время не рекомендуется использовать порядок потребления во время пересмотра спецификации.

Ответ 2

Это довольно сложный вопрос. Попытайтесь читать http://en.cppreference.com/w/cpp/atomic/memory_order несколько раз, попробуйте прочитать другие ресурсы и т.д.

Здесь приведено упрощенное описание:

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

Обычно вы можете использовать блокировки для синхронизации. Проблема в том, что они медленные. Атомные операции выполняются намного быстрее, поскольку синхронизация происходит на уровне ЦП (т.е. ЦП гарантирует, что ни один другой поток, даже на другом ЦП, не изменяет некоторую переменную и т.д.).

Итак, единственной проблемой, с которой мы сталкиваемся, является переупорядочение доступа к памяти. Переменная memory_order указывает, какие типы компиляторов переупорядочения должны быть запрещены.

relaxed - никаких ограничений.

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

acquire - никакие нагрузки не могут быть переупорядочены по адресу. атомная нагрузка. То есть если они находятся после атомной нагрузки в исходном коде, они будут происходить и после атомной нагрузки.

release - никакие магазины не могут быть переупорядочены по адресу. атомный магазин. То есть если они находятся перед атомным хранилищем в исходном коде, они будут происходить и перед атомным хранилищем.

acq_rel - acquire и release в сочетании.

seq_cst - сложнее понять, почему этот заказ требуется. В принципе, все остальные упорядочения гарантируют, что определенные запрещенные переупорядочения не будут выполняться только для потоков, которые потребляют/выпускают одну и ту же атомную переменную. Доступ к памяти может по-прежнему распространяться на другие потоки в любом порядке. Это упорядочение гарантирует, что этого не произойдет (таким образом, последовательная согласованность). Для случая, когда это необходимо, см. Пример в конце связанной страницы.