Подтвердить что ты не робот

Многопоточная паранойя

Это сложный вопрос, пожалуйста, тщательно подумайте, прежде чем отвечать.

Рассмотрим эту ситуацию. Два потока (читатель и писатель) получают доступ к одному глобальному int. Это безопасно? Обычно, я бы ответил без мысли, да!

Однако мне кажется, что Герб Саттер так не думает. В своих статьях об эффективном concurrency он обсуждает ошибочную блокировку очереди и исправленная версия.

В конце первой статьи и в начале второго он обсуждает редко рассматриваемый признак переменных, записывает порядок. Int являются атомарными, хорошими, но ints необязательно упорядочены, что может уничтожить любой алгоритм без блокировки, включая мой сценарий выше. Я полностью согласен с тем, что единственный способ гарантировать правильное многопоточное поведение на всех существующих и будущих платформах - использовать атоматику (блокировки памяти AKA) или мьютексы.

Мой вопрос; является ли повторная запись проблемы на реальном оборудовании? Или многопоточная паранойя просто педантична?
Как насчет классических однопроцессорных систем?
Что относительно более простых RISC-процессоров, таких как встроенный power-pc?

Уточнение: меня больше интересует то, что сказал г-н Саттер о записи переменных переупорядочения аппаратного обеспечения (процессор/кеш). Я могу остановить оптимизатор от нарушения кода с помощью переключателей компилятора или ручной проверки сборки после компиляции. Тем не менее, я хотел бы знать, может ли аппаратное обеспечение испортить код на практике.

4b9b3361

Ответ 1

Ваша идея проверки сборки недостаточно хороша; переупорядочение может происходить на аппаратном уровне.

Чтобы ответить на ваш вопрос, "это всегда проблема с чтением аппаратного обеспечения:" Да! На самом деле я сам столкнулся с этой проблемой.

Можно ли обойти проблему с однопроцессорными системами или другими ситуациями с особым случаем? Я бы сказал "нет", потому что через пять лет вам, возможно, придется работать на многоядерных серверах, а затем найти все эти места будет сложно (невозможно?).

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

Ответ 2

Yup - использовать барьеры памяти для предотвращения переупорядочения команд там, где это необходимо. В некоторых компиляторах С++ ключевое слово volatile было расширено для вставки неявных барьеров памяти для каждого чтения и записи, но это не переносное решение. (Аналогично с API-интерфейсом Interlocked * win32). Vista даже добавляет новые более тонкие интерфейсные API, которые позволяют вам определять семантику чтения или записи.

К сожалению, у С++ есть такая свободная модель памяти, что любой вид такого кода в какой-то мере будет не переносимым, и вам придется писать разные версии для разных платформ.

Ответ 3

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

Ответ 4

Мы столкнулись с проблемой, хотя и на процессорах Itanium, где переупорядочение команд более агрессивное, чем x86/x64.

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

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

Ответ 5

Неужели это проблема на реальном оборудовании?

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

Цитата из упомянутой статьи Херба Саттера (вторая)

Упорядоченные атомные переменные различаются по-разному на популярных платформах и средах. Например:

  • volatile в С#/. NET, как в volatile int.
  • volatile или * Atomic * в Java, как в volatile int, AtomicInteger.
  • atomic<T> в С++ 0x, предстоящий стандарт ISO С++, как в atomic<int>.

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

Ответ 6

Это проблема с реальным оборудованием. Мой друг работает для IBM и зарабатывает на жизнь прежде всего, рассматривая эту проблему в кодах клиентов.

Если вы хотите увидеть, как могут быть плохие вещи, найдите академические документы на модели памяти Java (а также теперь модель памяти С++). Учитывая переупорядочение, которое может сделать реальное оборудование, пытаясь понять, что безопасно на высоком уровне - это кошмар.

Ответ 7

Нет, это небезопасно, и существует реальная аппаратная avaialble, которая проявляет эту проблему, например, модель памяти в чипе powerpc на xbox 360 позволяет переупорядочивать записи. Это усугубляется отсутствием барьеров во внутренности, более подробно см. Эту статью в msdn.

Ответ 8

Ответ на вопрос "безопасен ли он" по своей сути неоднозначен.

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

"Atomic" означает, что вы получаете вторую гарантию. Поскольку double обычно не является атомарным, вы можете получить 32 старых и 32 новых бита. Это явно небезопасно.

Ответ 9

Когда я задал вопрос, который больше всего интересовал однопроцессорный powerpc. В одном из комментариев InSciTek Jeff упоминаются инструкции powerpc SYNC и ISYNC. Те, где ключ к окончательному ответу. Я нашел здесь на сайте IBM.

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