Являются ли + =, | =, & = etc атомарными? - PullRequest
32 голосов
/ 18 марта 2010

Являются ли операторы "модифицирования", такие как +=, |=, &= и т. Д. Атомарными?

Я знаю, ++ является атомарным (если вы выполните x++; в двух разных потоках "одновременно", вы всегда получите x, увеличенное на 2, в отличие от x=x+1 с отключенной оптимизацией.)

Что мне интересно, так это variable |= constant, а лайки поточно-ориентированы или я должен защищать их мьютексом?

(... или это зависит от процессора? В этом случае, как это на ARM?)

Ответы [ 12 ]

75 голосов
/ 18 марта 2010

Вы не правы. Нет никакой гарантии, что ++ является атомарным и не существует ни для составных операторов присваивания, ни для любой операции C ++.

9 голосов
/ 18 марта 2010

x++ часто реализуется в 3 инструкциях: прочитать X в регистр, увеличить его и записать обратно в память.

Ваша нить может быть прервана между любыми из них.

6 голосов
/ 18 марта 2010

Чтобы изменение значения было видно по ядрам, + = (например) должно было бы загрузить значение, добавить приращение и затем сохранить его. Это означает, что операция не будет атомарной .

Чтобы обеспечить атомарность, вам необходимо установить соответствующую блокировку вокруг операции.

3 голосов
/ 18 марта 2010

Нет, они не атомные! Если вам нужны атомарные операции с примитивными типами, и вы используете Linux, вы можете посмотреть здесь: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html и / или atomic.h ...

2 голосов
/ 30 апреля 2010

Это зависит как от компилятора, так и от процессора. Некоторые наборы инструкций предоставляют атомарные инструкции для них (для машинных размеров).

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


Специально для ARM: Инструкции ORR / ADD / AND берут два операнда и помещают результат в регистр. Любой операнд может быть тем же регистром, что и регистр результата, поэтому их можно использовать как атомарный | =, + =, & =.

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

2 голосов
/ 18 марта 2010

Нет оператора в C или C ++ гарантированно будет атомарным. Они могут быть на вашей платформе, но вы не будете знать наверняка. Как правило, единственной атомарной операцией является инструкция Test and Set, которая обычно доступна на большинстве современных процессоров в той или иной форме в качестве основы для реализации семафоров.

2 голосов
/ 18 марта 2010

++ может быть атомарным на вашем компиляторе / платформе, но в спецификациях c ++ он не определен как атомарный.

Если вы хотите изменить атомарное значение, вам следует использовать подходящие методы, такие как Interlocked * в Windows.

То же самое для всех других процедур. Если вы хотите атомарные операции, вы должны использовать соответствующие вызовы, а не стандартные.

1 голос
/ 26 октября 2017

A duplicate было направлено сюда, и это требует обновления. «Новый» язык C11 допускает атомарный атрибут, который допускает, что:

_Atomic int a;
...
a += 3

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

1: в некоторых архитектурах атомарные операции возможны только в памяти, которая поддерживает определенные протоколы доступа. ARMv7, MIPS, например, превратить последовательность в:

do {
    x = LoadLinked(a) + 3;
} while !StoreConditional(x, &a);

но LoadLinked / StoreConditional не определено для некоторых типов памяти / кэша. Наслаждайтесь отладкой этого.

2: Связано это ложное совместное использование , которое является артефактом LoadLinked, StoreConditional, работающего со строками кэша (например, 32, 64, 256 байтов), а не с субблоками. Так: _Atomic int a [4]; может потребоваться 4 * размер строки кэша (таким образом, 1024 байта), чтобы безопасно разрешить одновременные атомарные операции над [n] и a [n + 1], потому что 4 процессора могли бороться за обновление [0..3], но никогда не выполнялись ,

Надеемся, что следующий стандарт распознает внутреннюю ошибку декорации атрибута и восстановит c89 как законный стандарт C.

1 голос
/ 03 сентября 2013

Мало того, что они не атомарны, как все операции, но они могут иметь очень интересные результаты.Например, если компилятор видит, что он записывает в x, ему разрешается использовать x в качестве временной переменной, а не использовать регистры или пространство стека.Это означает, что x может временно содержать ЛЮБОЕ значение, а не только значения, которые имеют смысл для x

http://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong

0 голосов
/ 30 апреля 2010

Вы должны защитить свою переменную, например, с мьютексом

...