`memory_order_relaxed` необходим для предотвращения частичного чтения атомарных хранилищ - PullRequest
0 голосов
/ 20 октября 2019

Предположим, что поток 1 выполняет атомарные хранилища для переменной v, используя memory_order_release (или любой другой порядок), а поток 2 выполняет атомарные чтения для v, используя memory_order_relaxed.

. в этом случае невозможно иметь частичное чтение. Примером частичного чтения будет чтение первой половины v из последнего значения и второй половины v из старого значения.

  1. Если поток 2 теперь читает v безиспользуя атомарные операции, можем ли мы иметь частичное чтение в теории?
  2. Можем ли мы иметь частичное чтение на практике? (Спрашиваю, потому что я думаю, что это не должно иметь значения на большинстве процессоров, но я не уверен.)

1 Ответ

2 голосов
/ 20 октября 2019

Для 1. как вы предлагаете это сделать?

atomic<T> v - это шаблон, который перегружает неявное преобразование T(), чтобы быть похожим на .load(mo_seq_cst). Это делает разрывание невозможным. seq_cst atomic похож на расслабленный плюс некоторые гарантии упорядочения.

Шаблон также перегружает операторы, такие как ++, для создания атомарного .fetch_add(1, mo_seq_cst). (Или для предварительного увеличения, 1 + fetch_add для получения уже увеличенного значения).


Конечно, если вы посмотрите на байты объектного представления atomic<T>читая его с неатомарным char* (например, с memcpy(&tmp, &v, sizeof(int)), это UB, если другой поток модифицирует его . И да, вы можете получить разрыв на практике в зависимости от того, как вы это делаете.

Скорее всего для объектов, слишком больших, чтобы быть свободными от блокировки, но возможно в некоторых реализациях, например, для 8-байтовых объектов в 32-битной системе, которая может реализовать 8-байтовую атомарность со специальными инструкциями, но обычно просто используетдве 32-битные загрузки.

например, 32-битная x86, где атомарная 8-байтовая загрузка может быть выполнена с SSE, а затем возвращена обратно к целочисленным регистрам или lock cmpxchg8b. Компиляторы не делают этого, когдаим просто нужны два целочисленных регистра.

Но многие 32-разрядные RISC, которые обеспечивают атомарную 8-байтовую загрузку, имеют нагрузку с двойным регистром, которая создает 2 выходных регистра из одной инструкции, например ARM ldrd или MIPS ld. Компиляторы do используйте их для оптимизации выровненных 8-байтовых нагрузок, даже когда атомарность не является целью, так что вы, вероятно, "повезете" и не заметите разрыв в любом случае.

Маленькие объекты обычнов любом случае быть атомным;см. Почему целочисленное присваивание для естественно выровненной переменной атомарно в x86?


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

...