Ответ 1
-
Да, возможно спекулятивное задание. Модификация энергонезависимой переменной не является частью наблюдаемого поведения программы, и поэтому допускается ложная запись. (Ниже приведено определение "наблюдаемого поведения", которое фактически не включает в себя все поведение, которое вы могли бы наблюдать.)
-
Нет. Если
output
volatile
, спекулятивные или ложные мутации не допускаются, поскольку мутация является частью наблюдаемого поведения. (Запись в - или чтение с - аппаратный регистр может иметь последствия, кроме как только сохранение значения. Это один из основных вариантов использованияvolatile
.) -
(Отредактировано) Нет, спекулятивное назначение невозможно с помощью
atomic
output
. Нагрузки и хранилища переменныхatomic
являются синхронизированными операциями, поэтому не должно быть возможности загрузить значение такой переменной, которое не было явно сохранено в переменной.
Наблюдаемое поведение
Хотя программа может делать много явно видимых вещей (например, внезапно заканчивая из-за segfault), стандарты C и С++ гарантируют ограниченный набор результатов. Наблюдаемое поведение определено в проекте C11 в разделе 5.1.2.3p6 и в текущем проекте С++ 14 в разделе 1.9p8 [intro.execution] с очень похожей формулировкой:
Наименьшие требования к соответствующей реализации:
- Доступ к неустойчивым объектам оценивается строго в соответствии с правилами абстрактной машины.
- При завершении программы все данные, записанные в файлы, должны быть идентичны одному из возможных результатов выполнения программы в соответствии с абстрактной семантикой.
- Динамика ввода и вывода интерактивных устройств должна происходить таким образом, чтобы запрос вывода был фактически доставлен до того, как программа ждет ввода. Что представляет собой интерактивное устройство, оно определяется реализацией.
В совокупности они называются наблюдаемым поведением программы.
Вышеизложенное взято из стандарта С++; стандарт C отличается тем, что во второй точке он не допускает нескольких возможных результатов, а в третьей точке он явно ссылается на соответствующий раздел стандартных требований к библиотеке. Но детали в сторону, определения координируются; для цели этого вопроса важно, чтобы наблюдался только доступ к изменчивым переменным (вплоть до того, что значение энергонезависимой переменной отправляется на выходное устройство или файл).
Гонки данных
Этот абзац также должен читаться в общем контексте стандартов C и С++, которые освобождают реализацию от всех требований, если программа порождает поведение undefined. Поэтому segfault не рассматривается в определении наблюдаемого поведения выше: segfault - это возможное поведение undefined, но не возможное поведение в соответствующей программе. Таким образом, во вселенной только совместимых программ и соответствующих реализаций нет segfaults.
Это важно, потому что программа с расчетом данных не соответствует. Гонка данных имеет поведение undefined, даже если это кажется безобидным. И поскольку разработчик должен избегать поведения undefined, реализация может оптимизироваться без учета расы данных.
Изложение модели памяти в стандартах C и С++ является плотным и техническим, и, вероятно, не подходит для введения в концепции. (Просмотр материала на сайт Hans Boehm, вероятно, окажется менее трудным.) Извлечение котировок из стандарта является рискованным, потому что детали важны. Но вот небольшой прыжок в болото, из текущего стандарта С++ 14, & sect; 1.10 [intro.multithread]:
- Две оценки выражений конфликтуют, если один из них изменяет местоположение памяти, а другой читает или изменяет одно и то же место в памяти.
& hellip;
Два действия потенциально параллельны, если
- они выполняются разными потоками или
- они не подвержены последовательности, и по меньшей мере один выполняется обработчиком сигналов.
Выполнение программы содержит гонку данных, если она содержит два потенциально параллельных конфликтных действия, по крайней мере один из которых не является атомарным, и не происходит до другого, за исключением специального случая для обработчиков сигналов, описанных ниже. Любая такая гонка данных приводит к поведению undefined.
Вывод здесь состоит в том, что чтение и запись одной и той же переменной необходимо синхронизировать; в противном случае это гонка данных, а результат - undefined. Некоторые программисты могут возражать против строгости этого запрета, утверждая, что некоторые расы данных являются "доброкачественными". Это тема Газета Hans Boehm 2011 HotPar "Как неправильно компрометировать программы с" доброкачественными "расами данных" (pdf) (резюме автора: "Там не являются доброкачественными расами данных" ), и он объясняет это намного лучше, чем мог.
Синхронизация здесь включает в себя использование типов atomic
, так что не гонка данных одновременно считывает и изменяет переменную atomic
. (Результат чтения непредсказуем, но он должен быть либо значением до модификации, либо значением после этого.) Это предотвращает выполнение компилятором "поэтапной" модификации атомной переменной без какой-либо явной синхронизации.
После некоторых размышлений и исследований мы пришли к выводу, что компилятор не может выполнять спекулятивную запись и для атомных переменных. Следовательно, я изменил ответ на вопрос 3, на который я первоначально ответил "нет".
Другие полезные ссылки:
-
Bartosz Milewski: Работа с Benign Data Races С++ Way
Милевски имеет дело с точной проблемой спекулятивных записей для атомных переменных и заключает:
Не может ли компилятор сделать тот же самый грязный трюк и мгновенно сохранить 42 в переменной
owner
? Нет, это не может! Поскольку переменная объявлена atomic
, компилятор больше не может предполагать, что запись can not может наблюдаться другими потоками. -
Herb Sutter on Безопасность потока и синхронизация
Как обычно, доступно и хорошо написанное объяснение.