Является ли volatile правильным способом сделать один байт атомарным в C / C ++? - PullRequest
11 голосов
/ 08 февраля 2011

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

Или другими словами: процессоры всегда считывают и записывают байты атомарно?

Ответы [ 5 ]

21 голосов
/ 08 февраля 2011

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

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

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

5 голосов
/ 08 февраля 2011

Стандарт ничего не говорит об атомарности.

4 голосов
/ 08 февраля 2011

На любом нормальном процессоре чтение и запись любого выровненного текста размером с слово или меньше является атомарным. Это не проблема. Вопросы:

  • То, что чтение и запись являются атомарными, не означает, что последовательности чтения / изменения / записи являются атомарными. В языке C x++ концептуально представляет собой цикл чтения / изменения / записи. Вы не можете контролировать, генерирует ли компилятор атомарный прирост, и в общем случае это не так.
  • Проблемы с синхронизацией кэша. В архитектурах на полпути (почти все, что не x86) аппаратное обеспечение слишком тупое, чтобы гарантировать, что представление памяти, которое видит каждый процессор, отражает порядок, в котором происходит запись. Например, если процессор 0 выполняет запись по адресам A, а затем B, вполне возможно, что процессор 1 видит обновление по адресу B, но не обновление по адресу A. Для решения этой проблемы требуются специальные коды защиты памяти / барьера, и компилятор не будет генерировать их для вас.

Второй пункт имеет значение только для SMP / многоядерных систем, поэтому, если вы довольны ограничением себя в одноядерности, вы можете проигнорировать его, и тогда plain читает и записывает будет атомарным в C на любая здравомыслящая архитектура процессора. Но вы не можете сделать много полезного только с этим. (Например, единственный способ реализовать простую блокировку таким способом - это O(n) пробел, где n - количество потоков.)

4 голосов
/ 08 февраля 2011

Ключевое слово volatile используется для указания того, что переменной (int, char или другим) может быть присвоено значение из внешнего непредсказуемого источника. Это обычно удерживает компилятор от оптимизации переменной.

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

3 голосов
/ 08 февраля 2011

Короткий ответ : Не используйте volatile для гарантии атомарности.

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

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

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

Как правило, они предотвращают неупорядоченные операции чтения и записи, вызывая инструкцию Data Memory Barrier (DMB on ARM), которая гарантирует, что чтение и запись происходят в правильном порядке.Смотрите здесь для получения более подробной информации.

Другая проблема с volatile заключается в том, что он не даст компилятору оптимизировать, даже когда это совершенно нормально.

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