Являются ли +=, |=, &= и т. д. атомарными?

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

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

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

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


person SF.    schedule 18.03.2010    source источник
comment
Какой АРМ? Архитектура v6 (ARM10) и более поздние версии могут обеспечивать атомарные операции, если компилятор поддерживает это или вы создаете собственную сборку. Более ранние архитектуры не могут.   -  person Mike Seymour    schedule 18.03.2010
comment
gcc имеет встроенные атомарные операции: gcc. gnu.org/onlinedocs/gcc-4.4.3/gcc/ ; обратите внимание: не все операции поддерживаются всеми целевыми процессорами   -  person Christoph    schedule 18.03.2010
comment
есть Windows API для заблокированного доступа к переменным: msdn. microsoft.com/en-us/library/ms684122%28v=VS.85%29.aspx   -  person Janusz Lenar    schedule 18.03.2010
comment
Помимо того, что язык этого не гарантирует, x86-64 имеет сильный режим работы с памятью (загружает эквайры и сохраняет релизы), и даже там, например. INC m64 как операция RMW не гарантируется атомарностью, если только не используется префикс LOCK.   -  person Arne Vogel    schedule 16.11.2018


Ответы (12)


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

person Community    schedule 18.03.2010
comment
Это означает, что это зависит от процессора. В одноядерных архитектурах, допускающих inc [address], это определенно атомарно. - person SF.; 18.03.2010
comment
По крайней мере, до C++0x: в этом пункте описываются компоненты для мелкозернистого атомарного доступа. Этот доступ предоставляется посредством операций над атомарными объектами. [29.1/1 в проекте N3035]. - person ; 18.03.2010
comment
@SF Нет, это не так. Это зависит от компилятора. Тот факт, что в архитектуре ЦП есть инструкция, не означает, что компилятор будет использовать ее так, как вы думаете, если он вообще ее использует. - person ; 18.03.2010
comment
Даже если компилятор может генерировать атомарное приращение, может ли x++ быть атомарным, может зависеть от типа данных x. Например, для цели SF приращение x, если x имеет значение «long long», не будет атомарным, а если бы это был другой целочисленный тип, это, вероятно, может зависеть от конкретной архитектуры ARM и выравнивания данных. - person Clifford; 18.03.2010
comment
@SF: Это очень конкретно. Что, если компилятор решит не использовать эту инструкцию? Что если/когда вы перейдете на многоядерную систему? Как насчет архитектур, в которых нет инструкции inc? Вообще ++ не атомарно. Вы только что нашли один единственный узкий частный случай, когда это не проблема. - person jalf; 18.03.2010
comment
@SF.: Я даже не думаю, что inc [address] является атомарным на x86. - person user541686; 03.09.2013

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

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

person Alex Budovski    schedule 18.03.2010
comment
На некоторых процессорах даже отдельные инструкции могут быть неатомарными. - person Jive Dadson; 19.08.2012

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

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

person Benno    schedule 18.03.2010

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

person DavideRizzi    schedule 18.03.2010

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

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

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

person Sam    schedule 18.03.2010

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

person plinth    schedule 18.03.2010

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

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


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

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

person JoeG    schedule 30.04.2010

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

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

person Cort Ammon    schedule 03.09.2013

Сюда направлен дубликат, и его необходимо обновить. «Новый» язык 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] и [n + 1], потому что 4 процессора могут бороться за обновление [0..3], но никогда не удается .

Будем надеяться, что следующий стандарт признает врожденную неудачу оформления атрибутов и восстановит c89 в качестве законного стандарта C.

person mevets    schedule 26.10.2017

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

person Oddthinking    schedule 18.03.2010
comment
Они не могут быть перегружены для целых чисел, о чем, я думаю, и идет речь. - person ; 18.03.2010
comment
Согласен, но об этом не говорится, поэтому я счел это достойным упоминания. - person Oddthinking; 18.03.2010

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

person fredoverflow    schedule 18.03.2010

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

person Nikko    schedule 30.04.2010