Сделайте я должен использовать блокировку с целыми числами в потоках C++

Если я получаю доступ к единственному целому типу (например, долго, интервал, bool, и т.д....) в нескольких потоках, сделайте я должен использовать механизм синхронизации, такой как взаимное исключение для блокировки их. Мое понимание - то, что как атомарные типы, я не должен блокировать доступ к единственному потоку, но я вижу много кода там, который действительно использует блокировку. Профилирование такого кода показывает, что существует значительный хит производительности для использования блокировок, таким образом, я быть бы нет. Таким образом, если объект, к которому я получаю доступ, соответствует целому числу ширины шины (например, 4 байта на процессоре на 32 бита), я должен заблокировать доступ к нему, когда он используется через несколько потоков? Другими словами, если поток A запись к целочисленной переменной X в то же время, что и поток B читает из той же переменной, действительно ли возможно, что поток B мог закончиться несколько байтов предыдущего значения, смешанного в с несколькими байтами записанного значения? Действительно ли это является архитектурно-зависимым, например, хорошо для 4-байтовых целых чисел в системах на 32 бита, но небезопасным на 8-байтовых целых числах в системах на 64 бита?

Править: Просто видел это связанное сообщение, которое помогает немного.

8
задан Community 23 May 2017 в 10:24
поделиться

7 ответов

Вы никогда не блокируете значение - вы блокируете операцию на значении.

C и C ++ явно не упоминают потоки или атомарные операции, поэтому операции, которые выглядят так, как будто они могут или должны быть атомарными, не гарантируются спецификацией языка как атомарные.

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

Такие простые операции, как m_counter ++ , включают операцию выборки, увеличения и сохранения - состояние гонки: другой поток может изменить значение после выборки, но до сохранения - и, следовательно, должен быть защищен mutex - ИЛИ найдите, что ваш компилятор поддерживает взаимосвязанные операции. В MSVC есть такие функции, как _InterlockedIncrement (), которые будут безопасно увеличивать ячейку памяти, если все другие записи аналогичным образом используют взаимосвязанный API для обновления ячейки памяти, что на несколько порядков легче, чем вызов даже критического раздела.

GCC имеет встроенные функции, такие как __ sync_add_and_fetch , которые также могут использоваться для выполнения взаимосвязанных операций со значениями машинного слова.

8
ответ дан 5 December 2019 в 06:09
поделиться

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

Если это платформа Windows, вы также можете проверить здесь: Interlocked Variable Access .

3
ответ дан 5 December 2019 в 06:09
поделиться

Многопоточность - это сложно и сложно. Количество трудно диагностируемых проблем, которые могут возникнуть, довольно велико. В частности, на архитектурах Intel чтение и запись из выровненных 32-битных целых чисел гарантированно будет атомарным в процессоре, но это не означает, что это безопасно делать в многопоточных средах.

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

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

2
ответ дан 5 December 2019 в 06:09
поделиться

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

4
ответ дан 5 December 2019 в 06:09
поделиться

Да. Если вы работаете в Windows, вы можете взглянуть на Interlocked функции / переменные, а если вы убеждены в Boost, то можете посмотреть на их реализацию атомарных переменных .

Если ускорение слишком велико, добавление " atomic c ++ " в вашу любимую поисковую систему даст вам много пищи для размышлений.

2
ответ дан 5 December 2019 в 06:09
поделиться

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

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

См .:

  1. Код без блокировки: ложное ощущение безопасности
  2. Потоки не могут быть реализованы как библиотека
4
ответ дан 5 December 2019 в 06:09
поделиться

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

  1. Вам нужно остановить компилятор от оптимизации фактической записи! (Это очень важно. ;-))
  2. Вам нужны барьеры памяти (а не то, что смоделировано в C), чтобы убедиться, что другие ядра обратили внимание на то, что вы что-то изменили. Иначе вы запутаетесь в кэшах между всеми процессорами и прочих грязных мелочах.

Если бы дело было только в первом, то можно было бы обойтись пометкой переменной volatile, но второе - это действительно убийца, и вы увидите разницу только действительно на многоядерной машине. Которая, как оказалось, является архитектурой, которая становится гораздо более распространенной, чем раньше... Упс! Пора перестать быть небрежным; используйте правильный код мьютекса (или синхронизации, или чего угодно) для вашей платформы, и все детали того, как заставить память работать так, как вы считаете нужным, исчезнут.

4
ответ дан 5 December 2019 в 06:09
поделиться
Другие вопросы по тегам:

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