Ответ 1
Вы ошибаетесь. Нет никакой гарантии, что ++ является атомарным и не существует для составных операторов присваивания или даже для любой операции на С++.
Являются ли операторы "изменить" такими, как +=
, |=
, &=
и т.д. atom?
Я знаю, что ++
является атомарным (если вы выполняете x++;
в двух разных потоках одновременно), вы всегда окажетесь x
увеличено на 2, а не x=x+1
, при выключенной оптимизации).
Интересно, существует ли variable |= constant
, а подобные - поточно-безопасные или мне нужно их защищать с помощью мьютекса?
(... или он зависит от процессора? В этом случае, как это делается на ARM?)
Вы ошибаетесь. Нет никакой гарантии, что ++ является атомарным и не существует для составных операторов присваивания или даже для любой операции на С++.
x++
часто реализуется в трех инструкциях: Прочитайте X в регистр, Increment it и запишите его обратно в память.
Ваша нить может быть предварительно удалена между любыми из них.
Для изменения значения, которое должно быть видимым по всем ядрам, a + = (например) должно было бы загрузить значение, добавить инкремент и затем сохранить его. Это означает, что операция не будет атомарной.
Чтобы обеспечить атомарность, вам необходимо установить соответствующую блокировку операции.
Нет, они не атомные! Если вам нужны атомарные операции над примитивными типами, и вы используете linux, вы можете посмотреть здесь: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html и/или атомный .h...
++ может быть атомарным на вашем компиляторе/платформе, но в спецификациях С++ он не определен как атомарный.
Если вы хотите, чтобы изменить значение в атомном режиме, вы должны использовать соответствующие методы, такие как Interlocked * на окнах.
То же самое для всех других подпрограмм. Если вам нужны атомные операции, вы должны использовать соответствующие вызовы, а не стандартные.
Нет на C или С++ гарантированно является атомарным. Они могут быть на вашей платформе, но вы не будете знать точно. Как правило, единственной операцией, которая является атомарной, является команда Test и Set, которая обычно доступна на большинстве современных процессоров в той или иной форме в качестве основы для реализации семафоров.
Это зависит как от компилятора, так и от процессора. Некоторые наборы инструкций предоставляют атомарные инструкции для этих (на машинных размерах).
Однако нет никакой гарантии, что вы компилятор будет использовать эти инструкции и не будет оптимизировать ваш код не-потоковым способом. Вам нужно либо писать процедуру в сборке, либо использовать специфичную для компилятора технику (например, instrinsics), которая обеспечивает атомарность (или использует библиотеку, которая использует один из этих методов).
В частности, на ARM: Команды ORR/ADD/AND принимают два операнда и помещают результат в регистр. Любой операнд может быть тем же регистром, что и регистр результатов, поэтому они могут использоваться как атомный | =, + =, & =.
Конечно, результат помещается в регистр, и первый операнд должен также поступать из регистра, поэтому вам нужно будет убедиться, что загрузка регистров выполняется атомарно.
Они не только атомарны, как и все операции, но могут иметь очень интересные результаты. Например, если компилятор видит, что он пишет в x, разрешено использовать x в качестве временной переменной, а не использовать регистры или пространство стека. Это означает, что x может временно содержать ЛЮБОЕ значение, а не только значения, которые имеют смысл для x
http://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong
Здесь был указан дубликат, и для этого требуется обновление. "Новый" язык C11 допускает атомный атрибут, который допускает следующее:
_Atomic int a;
...
a += 3
может быть скомпилирован в (атомный) неограниченный цикл. Спасибо за дар стандартов, я очень хочу, чтобы вы не имели.
1: в некоторых архитектурах атомные операции возможны только в памяти, которая поддерживает определенные протоколы доступа. ARMv7, MIPS, например, превращают последовательность в:
do {
x = LoadLinked(a) + 3;
} while !StoreConditional(x, &a);
но LoadLinked/StoreConditional - undefined для некоторых типов памяти/кеша. Наслаждайтесь отладкой.
2: Связано это ложное совместное использование, которое является артефактом LoadLinked, StoreConditional работает в строках кэша (например, 32, 64, 256 байт), а не подблоках. Так: _Atomic int a [4]; может потребоваться размер строки в 4 * кеша (таким образом, 1024 байта), чтобы безопасно разрешать одновременные атомарные операции на [n] и [n + 1], поскольку 4 cpus могут приближаться к обновлению [0..3], но никогда не удается,
Надеемся, что следующий стандарт распознает присущий им отказ от украшения атрибута и восстановит c89 как законный стандарт C.
Стоит отметить, что эти операторы могут быть перегружены, поэтому, безусловно, нет никакой общей гарантии, что они являются атомарными для всех классов.
Даже если ++
является атомной операцией, это не означает, что два потока, выполняющие ++x
, приведут к тому, что x
будет ровно на два выше. Вам нужно как-то синхронизировать потоки, иначе они не обязательно будут видеть изменения друг друга.
Вы должны защитить свою переменную, например, мьютексом