Чтение / запись int на компьютере x86 без блокировки - PullRequest
3 голосов
/ 24 марта 2011

Предположим, что в программе на C у меня есть P-потоки, работающие на 32-битной машине, и int MAX - общее 32-битное целое число

Каждый поток может читать / записывать в MAX.

Требование: значение, которое считывание потока не должно быть повреждено, например, первые 16 бит и последние 16 бит не синхронизированы

Вопрос: Нужна ли блокировка длязащитить чтение и запись?или Можно ли безопасно игнорировать блокировку, потому что инструкция сборки LOAD / SAVE гарантированно будет выполняться атомарно?

Ответы [ 6 ]

3 голосов
/ 24 марта 2011

Проблема здесь - чтение-изменение-запись.

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

Вы потеряете одно из обновлений.

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

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

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

Представьте, что один поток собирается записать значение, а затем установить флаг. Другой поток увидит поднятый флаг и затем прочитает значение. Эти потоки, если они находятся на отдельных ядрах, будут переупорядочивать свои потоки команд только в отношении самих себя - ядра не будут учитывать другие потоки, потому что они не могут - они не знают о них.

Таким образом, в первом потоке настройку флага можно переупорядочить, чтобы она произошла за до записи значения - в конце концов, для только для этого потока это переупорядочение хорошо. Эти две переменные полностью отключены. Там нет упорядочения зависимости. Зависимость существует в другом потоке на другом ядре , поэтому наше ядро ​​не может знать об этом.

Другой поток, конечно, увидит поднятый флаг и прочитает его, хотя запись на самом деле еще не произошла.

3 голосов
/ 24 марта 2011

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

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

2 голосов
/ 24 марта 2011

Большинство (все?) Современных процессоров имеют специальные инструкции, гарантирующие атомарный доступ к переменным такого типа.C теперь имеет стандартизированный интерфейс к этим инструкциям.К сожалению, это не полностью реализовано большинством компиляторов.

В настоящее время вы должны использовать расширения.Например, у gcc есть целых групп .Если вы смотрите по сторонам в Интернете, вы также должны легко найти реализации со встроенными инструкциями по сборке, которые можно использовать с другими компиляторами.

2 голосов
/ 24 марта 2011

Различные архитектуры / процессоры имеют разные гарантии атомарности. C предназначен для переносимости, поэтому вы не должны делать никаких предположений о том, какие гарантии атомарности дают какие-либо конкретные архитектуры / процессоры. Это означает, что даже для атомарного чтения / записи вы должны использовать некоторую абстракцию (например, библиотека, которая обеспечивает необходимые атомарные операции для каждой отдельной архитектуры).

Насколько я знаю (что не так уж много - большая часть моего опыта только с 80x86), для большинства архитектур чтение и запись по выровненным адресам, размер которых меньше некоторого минимального размера, обычно гарантированно являются атомарными ( где «некоторый минимальный размер» может быть размером регистра общего назначения, размером строки кэша или чем-то еще).

Это НЕ включает модификации (например, инструкции / операции, которые читают, изменяют, а затем записывают в адрес). Для переменной "int MAX" (в отличие от чего-то вроде "const int MAX = 1234") я бы предположил, что вы захотите сделать что-то вроде "if (foo> MAX) MAX = foo;" и вам понадобится более надежная атомарная операция (например, может быть атомарная «сравнить и поменять» в цикле, который повторяется, если сравнение было ложным).

Также не забудьте объявить вашу переменную как volatile.

  • Брендан
2 голосов
/ 24 марта 2011

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

0 голосов
/ 24 марта 2011

Я не уверен, что есть какая-либо явная гарантия, но пока значение хранится в правильно выровненной ячейке памяти (т.е. по адресу памяти, кратному 4), на практике у вас все будет в порядке;память читается / записывается только порциями как минимум такого размера.

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