Чтение и запись С++ int Atomic?

У меня есть два потока, один из которых обновляет int и один читает его. Это статистическое значение, в котором порядок чтения и записи не имеет значения.

Мой вопрос: нужно ли мне синхронизировать доступ к этому многобайтовому значению? Или, по-другому, может быть часть записи заполнена и прервана, а затем произойдет чтение.

Например, подумайте о значении = 0x0000FFFF, которое получает увеличенное значение 0x00010000.

Есть ли время, когда значение выглядит как 0x0001FFFF, о котором я должен беспокоиться? Конечно, чем больше тип, тем более вероятно что-то подобное.

Я всегда синхронизировал эти типы доступа, но мне было любопытно, что думает сообщество.

Ответ 1

Сначала можно подумать, что чтение и запись собственного размера машины являются атомарными, но есть ряд проблем, связанных с когерентностью кэша между процессорами/ядрами. Используйте атомные операции, такие как Interlocked * в Windows и эквивалент в Linux. С++ 0x будет иметь "атомный" шаблон, чтобы обернуть их в приятный и кросс-платформенный интерфейс. На данный момент, если вы используете уровень абстракции платформы, это может обеспечить эти функции. ACE, см. шаблон класса ACE_Atomic_Op.

Ответ 2

Мальчик, какой вопрос. Ответ на который:

Да, нет, хм, ну, это зависит

Все сводится к архитектуре системы. На IA32 правильно выровненный адрес будет представлять собой атомную операцию. Невыраженные записи могут быть атомарными, это зависит от используемой системы кеширования. Если память лежит в одной строке кеша L1, то она является атомарной, иначе это не так. Ширина шины между процессором и ОЗУ может влиять на атомную природу: правильно выровненная 16-битная запись на 8086 была атомарной, тогда как одна и та же запись на 8088 была не потому, что 8088 имела только 8-битную шину, тогда как 8086 16-разрядная шина.

Кроме того, если вы используете C/С++, не забывайте отмечать общее значение как volatile, в противном случае оптимизатор будет считать, что переменная никогда не обновляется в одном из ваших потоков.

Ответ 3

ЕСЛИ вы читаете/записываете 4-байтовое значение И оно выровнено по DWORD в памяти И вы работаете в архитектуре I32, ТОГДА чтение и запись являются атомарными.

Ответ 4

Да, вам нужно синхронизировать обращения. В С++ 0x это будет гонка данных и поведение undefined. С потоками POSIX это уже работает undefined.

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

Ответ 5

Вы должны синхронизировать, но на некоторых архитектурах есть эффективные способы сделать это.

Лучше всего использовать подпрограммы (возможно, замаскированные за макросами), чтобы вы могли условно заменить реализации на платформенные.

Ядро Linux уже имеет некоторый код.

Ответ 6

В Windows блокировка *** Exchange *** Добавить гарантированно будет атомарным.

Ответ 7

Чтобы повторить то, что все сказали наверху, язык pre-С++ 0x не может гарантировать ничего о доступе к общей памяти из нескольких потоков. Любые гарантии будут до компилятора.

Ответ 8

Нет, они не (или, по крайней мере, вы не можете предположить, что они есть). Сказав это, есть некоторые трюки, чтобы сделать это атомарно, но они обычно не переносимы (см. Compare-and-swap).

Ответ 9

Я согласен со многими и особенно Джейсоном. В окнах можно использовать InterlockedAdd и его друзей.

Ответ 10

Обратите внимание на проблему с кешем, упомянутую выше...

Если вы переносите код на процессор с меньшим размером регистра, он больше не будет атомарным.

IMO, проблемы с потоками слишком сложны, чтобы рисковать.

Ответ 11

Давайте рассмотрим этот пример

int x;
x++;
x=x+5;

Первый оператор считается атомарным, потому что он преобразуется в одну директиву сборки INC, которая принимает один цикл ЦП. Однако второе назначение требует нескольких операций, поэтому это явно не атомная операция.

Другой, например,

x=5;

Опять же, вам нужно разобрать код, чтобы увидеть, что именно происходит здесь.

Ответ 12

Т, Я думаю, что в тот момент, когда вы используете константу (например, 6), инструкция не будет завершена в течение одного машинного цикла. Попробуйте увидеть набор команд x + = 6 по сравнению с x ++

Ответ 13

Некоторые люди думают, что ++ c является атомарным, но следите за созданной сборкой. Например, с помощью "gcc -S":

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

Чтобы инкрементировать int, компилятор сначала загружает его в регистр и сохраняет его обратно в память. Это не атомный.

Ответ 15

Единственный переносимый способ - использовать тип sig_atomic_t, определенный в заголовке signal.h для вашего компилятора. В большинстве реализаций C и С++ это int. Затем объявите переменную как "volatile sig_atomic_t."