Для 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
в том смысле, что она всегда повторно обращается к памяти. (Конечно, с помощью связного аппаратного кэша, только не сохраняя значение в регистре.)