Блокировка мьютексов функций достаточно без изменчивости?

Мы с коллегой пишем мягко. ПО для различных платформ, работающих на x86, x64, Itanium, PowerPC и других серверных процессорах 10-летней давности.

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

int foo::bar()
{
 //...
 //code which may or may not access _protected.
 pthread_mutex_lock(m);
 int ret = _protected;
 pthread_mutex_unlock(m);
 return ret;
}

Меня беспокоит кеширование. Может ли компилятор поместить копию _protected в стек или в регистр и использовать это устаревшее значение в присваивании? Если нет, то что этому мешает? Уязвимы ли варианты этого шаблона?

Я предполагаю, что компилятор на самом деле не понимает, что pthread_mutex_lock () - это специальная функция, поэтому мы просто защищены точками последовательности?

Большое спасибо.

Обновление: Хорошо , Я вижу тенденцию с ответами, объясняющими, почему волатильность - это плохо. Я уважаю эти ответы, но статьи на эту тему легко найти в Интернете. То, что я не могу найти в Интернете, и причина, по которой я задаю этот вопрос, заключается в том, как я защищен без volatile. Если приведенный выше код правильный, как он неуязвим для проблем с кешированием?

39
задан starblue 9 October 2012 в 09:00
поделиться

1 ответ

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

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

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

я предполагаю, что компилятор на самом деле не понимает, что pthread_mutex_lock () является специальной функцией, также - мы просто защищены точками последовательности?

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

, Как это является "особенным"? Это непрозрачно и рассматривается как таковой. Это не является особенным среди непрозрачных функций .

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

Мое беспокойство кэшируется. Компилятор мог поместить копию _protected на стеке или в регистре и использовании, которые лишают значение новизны в присвоении?

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

Так да между вызовами к непрозрачным функциям . Не через.

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

В противном случае, что предотвращает это? Действительно ли изменения этого шаблона уязвимы?

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

положение по умолчанию компилятора всегда, "давайте выполнимся глупо, я не понимаю то, что делается так или иначе" не, "Я оптимизирую, которые переписывают алгоритм, который я знаю лучше". Большая часть кода не оптимизирована в комплексе не локальный путь.

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

  • функция "встроена" (= доступный для встраивания) (или глобальная оптимизация умирает, или все функции нравственно "встроены");
  • никакой барьер памяти не необходим (как в системе разделения по времени монопроцессора, и в многопроцессорной системе сильно заказанная система) в той примитивной синхронизации (блокируют или разблокировали), таким образом, она не содержит такой вещи;
  • существует никакая специальная инструкция (как, сравнивают и устанавливают) используемый (например, для спин-блокировки, разблокировать операция является простой записью);
  • существует никакой системный вызов , чтобы приостановить или разбудить потоки (не нужный в спин-блокировке);

затем у нас могла бы быть проблема, поскольку компилятор мог оптимизировать вокруг вызова функции . Это фиксируется тривиально путем вставки барьера компилятора, такого как пустой asm оператор с "ударением" для других доступных переменных. , Который означает, что компилятор просто предполагает, что что-либо, что могло бы быть доступно для вызванной функции, "ударено".

или должна ли защищенная переменная быть энергозависима.

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

Создание, это энергозависимый на самом деле даже не устранило бы проблему, описанную выше, поскольку энергозависимый по существу операция памяти в абстрактной машине, которая имеет семантику операции ввода-вывода , и как таковой только заказан относительно [1 139]

  • реальный ввод-вывод как системные вызовы iostream
  • другие энергозависимые операции
  • , asm память ударяет (но затем никакой побочный эффект памяти не переупорядочивается вокруг тех)
  • вызовы к внешним функциям (поскольку они могли бы сделать одно вышеупомянутое)

Энергозависимый, не заказан относительно побочных эффектов энергонезависимой памяти. , Который делает энергозависимым практически бесполезный (бесполезный для практических применений) для написания ориентированного на многопотоковое исполнение кода даже в наиболее конкретном случае, где энергозависимый, был бы априорный справка, случай, где никакой забор памяти никогда не необходим: при программировании поточной обработки примитивов в системе разделения по времени на единственном ЦП. (Который может быть одним из наименее понятых аспектов или C или C++.)

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

0
ответ дан 27 November 2019 в 02:52
поделиться
Другие вопросы по тегам:

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