Действительно ли безопасно считать целочисленную переменную, это одновременно изменяется без блокировки?

Вы могли бы хотеть посмотреть на библиотеки XSL-FO, которые являются там, который может сделать создание PDF как преобразование. Я попытаюсь найти ссылку.

46
задан Justin Johnson 15 March 2010 в 08:38
поделиться

12 ответов

атомарное чтение
Как было сказано ранее, это зависит от платформы. На x86 значение должно быть выровнено по 4-байтовой границе. Обычно для большинства платформ чтение должно выполняться в одной инструкции ЦП.

кэширование оптимизатора
Оптимизатор не знает, что вы читаете значение, измененное другим потоком. объявление значения volatile помогает в этом: оптимизатор будет выполнять чтение / запись в память для каждого доступа, вместо того, чтобы пытаться сохранить значение в кэше в регистре.

Кэш ЦП
Тем не менее, вы может прочитать устаревшее значение, поскольку на современных архитектурах у вас есть несколько ядер с индивидуальным кешем, который не синхронизируется автоматически. Вам нужен барьер для чтения памяти, обычно это инструкция для конкретной платформы.

[править] пожалуйста, также смотрите комментарии дрхирша.

34
ответ дан 26 November 2019 в 20:35
поделиться

Я бы рекомендовал в этом случае не полагаться ни на какой компилятор или архитектуру .
Если у вас есть несколько читателей и писателей (а не просто читатели или просто писатели), вам лучше синхронизировать их всех. Представьте, что ваш код запускает искусственное сердце кого-то, вы действительно не хотите, чтобы он считывал неправильные значения, и, конечно же, вы не хотите, чтобы электростанция в вашем городе работала «буум», потому что кто-то решил не использовать этот мьютекс. Спасите себя ночным сном на долгое время, синхронизируйте их.
Если у вас есть только одно чтение потока - вы можете использовать только один мьютекс, однако, если вы планируете использовать несколько читателей и несколько писателей, вам понадобится сложный фрагмент кода для его синхронизации. Мне еще предстоит увидеть хорошую реализацию блокировки чтения / записи, которая также была бы «честной».

8
ответ дан 26 November 2019 в 20:35
поделиться

Вы задаете вопрос о чтении переменной, а затем говорите об обновлении переменной, что подразумевает операцию чтения-изменения-записи.

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

Есть несколько (и редких) исключений:

  • Чтение не выровнено, например, доступ к 4-байтовому int по нечетному адресу. Обычно вам нужно заставить компилятор со специальными атрибутами выполнить некоторую несогласованность.
  • Размер int больше, чем естественный размер инструкций, например, с использованием 16-битных int на 8-битной архитектуре.
  • Некоторые архитектуры имеют искусственно ограниченная ширина автобуса. Я знаю только очень старые и устаревшие, например, 386sx или 68008.
15
ответ дан 26 November 2019 в 20:35
поделиться

Представьте, что вы читаете переменную в одном потоке, этот поток прерывается во время чтения, а переменная изменяется записывающим потоком. Каково же значение целого числа чтения после возобновления потока чтения?

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

Консенсус заключается в том, что вы должны инкапсулировать / блокировать все записи индивидуально, в то время как чтение может выполняться одновременно с (только) другими чтениями

5
ответ дан 26 November 2019 в 20:35
поделиться

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

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

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

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

3
ответ дан 26 November 2019 в 20:35
поделиться

Если вы не используете предыдущее значение этой переменной при записи новой, то:

Вы можете читать и записывать целочисленную переменную без использования мьютекса. Это потому, что целое число является базовым типом в 32-битной архитектуре, и каждое изменение / чтение значения выполняется за одну операцию.

Но если вы выполняете что-то вроде увеличения:

myvar++;

Тогда вам нужно использовать мьютекс, потому что эта конструкция расширяется myvar = myvar + 1 и между чтением myvar и увеличением myvar myvar можно изменить. В этом случае вы получите неверное значение.

2
ответ дан 26 November 2019 в 20:35
поделиться

Хотя, вероятно, было бы безопасно читать целые числа в 32-битных системах без синхронизации. Я бы не стал рисковать. Хотя множественные одновременные чтения не являются проблемой, я не хочу, чтобы запись происходила одновременно с чтением.

Я бы порекомендовал поместить операции чтения в критическую секцию, а затем провести нагрузочное тестирование приложения на нескольких ядрах, чтобы увидеть, не вызывает ли это слишком много конфликтов. Обнаружение ошибок параллелизма - это кошмар, которого я предпочитаю избегать. Что произойдет, если в будущем кто-то решит изменить int на long long или double, чтобы они могли хранить большие числа?

Если у вас есть хорошая библиотека потоков, такая как boost.thread или zthread, тогда вам следует прочитать / писатель замки. Они идеально подходят для вашей ситуации, поскольку позволяют выполнять несколько операций чтения при одновременной защите записи.

2
ответ дан 26 November 2019 в 20:35
поделиться

Это может произойти в 8-битных системах, использующих 16-битные целые числа.

Если вы хотите избежать блокировки, вы можете при подходящих обстоятельствах читать несколько раз, пока не получите два одинаковых подряд ценности. Например, я использовал этот подход для чтения 64-битных часов на 32-битной встроенной цели, где такт часов был реализован как процедура прерывания. В этом случае достаточно трехкратного чтения, потому что часы могут отсчитывать только один раз за короткое время выполнения процедуры чтения.

1
ответ дан 26 November 2019 в 20:35
поделиться

В общем, каждая машинная инструкция При выполнении проходит несколько аппаратных этапов. Поскольку большинство современных процессоров являются многоядерными или гиперпоточными, это означает, что чтение переменной может запустить ее перемещение по конвейеру команд, но не останавливает другое ядро ​​ЦП или гиперпоток от одновременного выполнения инструкции сохранения для того же адрес. Две одновременно выполняющиеся инструкции, чтение и сохранение, могут «пересекаться», Чтобы продолжить: вам нужен мьютекс как для чтения, так и для записи.

0
ответ дан 26 November 2019 в 20:35
поделиться

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

Я считаю, что критические разделы зависят от платформы. В Win32 критическая секция очень эффективна: при отсутствии блокировки вход в критическую секцию почти бесплатный и не влияет на общую производительность. Когда происходит блокировка, она по-прежнему более эффективна, чем мьютекс, поскольку выполняет серию проверок перед приостановкой потока.

0
ответ дан 26 November 2019 в 20:35
поделиться

Зависит от вашей платформы. Большинство современных платформ предлагают атомарные операции для целых чисел: в Windows есть InterlockedIncrement, InterlockedDecrement, InterlockedCompareExchange и т. Д. Эти операции обычно поддерживаются базовым оборудованием (читай: ЦП) и обычно дешевле, чем использование критической секции или других механизмов синхронизации.

См. MSDN: InterlockedCompareExchange

Я считаю, что Linux (и современные варианты Unix) поддерживают аналогичные операции в пакете pthreads, но я не утверждаю, что я в этом эксперт.

-1
ответ дан 26 November 2019 в 20:35
поделиться

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

Прочтите, что делает volatile, прежде чем слепо начать его использовать: http://msdn.microsoft.com/en-us/library/12a04hfd (VS.80) .aspx

-3
ответ дан 26 November 2019 в 20:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: