Работают ли атомарные операции в разных процессах так же, как они работают в разных потоках?
-
22-09-2019 - |
Вопрос
Очевидно, что атомарные операции гарантируют, что разные потоки не будут искажать значение.Но верно ли это по-прежнему для всех процессов при использовании общей памяти?Даже если процессы запланированы операционной системой для запуска на разных ядрах?Или на разных процессорах?
Редактировать:Кроме того, если это небезопасно, то разве это небезопасно даже в такой операционной системе, как Linux, где процессы и потоки одинаковы с точки зрения планировщика?
Решение
tl; dr: Прочтите мелкий шрифт в документации по атомарным операциям.Некоторые из них будут атомарными по своей конструкции, но могут спотыкаться о определенные типы переменных.В общем, однако, атомарная операция будет поддерживать свой контракт между различными процессами точно так же, как это происходит между потоками.
Атомарная операция действительно гарантирует только то, что у вас не будет несогласованного состояния, если вы вызываетесь двумя объектами одновременно.Например, атомарное приращение, вызываемое двумя разными потоками или процессами для одного и того же целого числа, всегда будет вести себя следующим образом:
- x = начальное значение (нулевое для целей этого обсуждения)
- Объект A увеличивает x и возвращает результат самому себе:результат = x = 1.
- Объект B увеличивает x и возвращает результат самому себе:результат = x = 2.
где A и B указывают на первый и второй поток или процесс, который выполняет вызов.
Неатомная операция может привести к противоречивым или вообще сумасшедшим результатам из-за условий гонки, неполной записи в адресное пространство и т.д.Например, вы можете легко увидеть это:
- x = начальное значение снова = нулю.
- Объект A вызывает x = x + 1.Чтобы вычислить x + 1, A проверяет значение x (ноль) и добавляет 1.
- Объект B вызывает x = x + 1.Чтобы вычислить x + 1, B проверяет значение x (по-прежнему равное нулю) и добавляет 1.
- Объект B (по счастливой случайности) финиширует первым и присваивает x результат x + 1 = 1 (шаг 3).x теперь равен 1.
- Объект A финиширует вторым и присваивает x результат x + 1 = 1 (шаг 2).x теперь равен 1.
Обратите внимание на условие гонки, поскольку объект B проходит мимо A и первым завершает выражение.
Теперь представьте, если бы x был 64-разрядным double, у которого не гарантировано наличие атомарных назначений.В этом случае вы могли бы легко увидеть что-то вроде этого:
- 64-битный double x = 0.
- Объект A пытается присвоить 0x1122334455667788 x.Первые 32 бита присваиваются первыми, оставляя x равным 0x1122334400000000.
- Объект B запускается и присваивает 0xffeeddccbbaa9988 x .Случайно обе 32-разрядные половины обновлены, и x теперь равен = 0xffeeddccbbaa9988.
- Объект A завершает свое назначение второй половиной, и x теперь равен = 0xffeeddcc55667788.
Эти неатомные назначения являются одними из самых отвратительных параллельных ошибок, которые вам когда-либо приходилось диагностировать.