Почему летучие существуют? - PullRequest
193 голосов
/ 16 сентября 2008

Что делает ключевое слово volatile? В C ++ какую проблему это решает?

В моем случае я никогда сознательно не нуждался в этом.

Ответы [ 16 ]

6 голосов
/ 16 сентября 2008
  1. вы должны использовать его для реализации спин-блокировок, а также некоторых (всех?) Структур данных без блокировки
  2. используйте его с атомарными операциями / инструкциями
  3. однажды помог мне преодолеть ошибку компилятора (неправильно сгенерированный код во время оптимизации)
4 голосов
/ 05 октября 2016

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

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

Рассмотрим следующие случаи

1) Глобальные переменные, измененные подпрограммой обработки прерываний вне области действия.

2) Глобальные переменные в многопоточном приложении.

Если мы не используем volatile квалификатор, могут возникнуть следующие проблемы

1) Код может не работать должным образом при включенной оптимизации.

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

Изменчивый: лучший друг программиста

https://en.wikipedia.org/wiki/Volatile_(computer_programming)

2 голосов
/ 30 июля 2018

В первые дни C компиляторы интерпретировали все действия, которые читают и записывают l-значения, как операции памяти, которые должны выполняться в той же последовательности, в которой операции чтения и записи появлялись в коде. Во многих случаях эффективность могла бы быть значительно улучшена, если бы компиляторам была предоставлена ​​определенная свобода для переупорядочения и консолидации операций, но с этим была проблема. Даже операции часто указывались в определенном порядке только потому, что было необходимо указывать их в некотором порядке, и, таким образом, программист выбрал одну из многих одинаково хороших альтернатив, что не всегда было так. Иногда было бы важно, чтобы определенные операции происходили в определенной последовательности.

Какие именно детали последовательности важны, зависит от целевой платформы и области применения. Вместо того, чтобы предоставлять особенно подробный контроль, Стандарт выбрал простую модель: если последовательность обращений выполняется с l-значениями, которые не квалифицированы volatile, компилятор может переупорядочить и консолидировать их по своему усмотрению. Если действие выполняется с volatile -качественным значением lvalue, качественная реализация должна предлагать любые дополнительные гарантии упорядочения при коде, нацеленном на его предполагаемую платформу и поле приложения, без необходимости использования нестандартного синтаксиса. *

К сожалению, вместо того, чтобы определять, какие гарантии понадобятся программистам, многие компиляторы предпочли вместо этого предложить минимальные гарантии, предусмотренные Стандартом. Это делает volatile гораздо менее полезным, чем должно быть. Например, в gcc или clang программист, которому нужно реализовать базовый «мьютекс передачи» [тот, в котором задача, которая получила и освободил мьютекс, не будет делать это до тех пор, пока другая задача не выполнит это], должен выполнить из четырех вещей:

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

  2. Определите все объекты, охраняемые мьютексом, как volatile - то, что не нужно, если все обращения происходят после получения мьютекса и перед его освобождением.

  3. Используйте уровень оптимизации 0, чтобы заставить компилятор генерировать код, как если бы все объекты, которые не квалифицированы register, были volatile.

  4. Используйте директивы, специфичные для gcc.

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

  1. Убедитесь, что volatile -качественная запись выполняется везде, где требуется приобретение или выпуск.

Для получения базового «мьютекса передачи» требуется чтение volatile (чтобы увидеть, готово ли оно), и не требуется также запись volatile (другая сторона не будет пытаться повторно получить пока он не будет возвращен), но выполнение бессмысленной записи volatile все же лучше, чем любой из параметров, доступных в gcc или clang.

2 голосов
/ 23 октября 2017

Ваша программа работает даже без ключевого слова volatile? Возможно, это причина:

Как упоминалось ранее, ключевое слово volatile помогает в таких случаях, как

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

Но, похоже, эффект почти не проявляется после вызова внешней или не встроенной функции. E.g.:

while( *p!=0 ) { g(); }

Затем с или без volatile генерируется почти одинаковый результат.

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

Но остерегайтесь того дня, когда ваша функция g () станет встроенной (либо из-за явных изменений, либо из-за ловкости компилятора / компоновщика), тогда ваш код может сломаться, если вы забудете ключевое слово volatile!

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

2 голосов
/ 17 сентября 2008

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

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

1 голос
/ 05 мая 2017

Следует напомнить, что в функции-обработчике сигналов вы хотите получить доступ / изменить глобальную переменную (например, пометить ее как exit = true), вы должны объявить эту переменную как 'volatile'.

...