volatile
говорит компилятору, что каждое чтение и запись имеет наблюдаемый побочный эффект;таким образом, компилятор не может делать какие-либо предположения о том, что два чтения или две записи в строке имеют одинаковый эффект.
Например, обычно следующий код:
int a = *x;
int b = *x;
if (a == b)
printf("Hi!\n");
Может бытьоптимизировано для:
printf("Hi!\n");
Что делает volatile
, так это сообщает компилятору, что эти значения могут приходить откуда-то вне контроля программы, поэтому он должен фактически прочитать эти значения и выполнить сравнение.
Многие люди ошиблись, думая, что они могут использовать volatile
для создания структур данных без блокировки, которые позволят нескольким потокам совместно использовать значения, и они могли наблюдать влияние этих значений в других потоках..
Однако volatile
ничего не говорит о том, как взаимодействуют разные потоки, и может применяться к значениям, которые могут кэшироваться с разными значениями на разных ядрах, или могут применяться к значениям, которые не могут быть записаны атомарнов одной операции, и поэтому, если вы пытаетесь написать многопоточный или многоядерный код, используя volatile
, выможет столкнуться с множеством проблем.
Вместо этого вам нужно либо использовать блокировки или какой-либо другой стандартный механизм параллелизма для связи между потоками, либо использовать барьеры памяти , либо использовать C11 / C ++11 атомарные типы и атомарные операции .Блокировки гарантируют, что весь регион кода имеет эксклюзивный доступ к переменной, которая может работать, если у вас есть значение, которое слишком велико, слишком мало или не выровнено для атомарной записи в одной операции, в то время как барьеры памяти и атомарные типыа операции обеспечивают гарантии того, как они работают с ЦП, чтобы гарантировать, что кэши синхронизируются или операции чтения и записи выполняются в определенных порядках.
В основном, volatile
в основном полезен, когда вы взаимодействуете с однимаппаратный регистр, который может изменяться вне контроля программ, но может не требовать каких-либо специальных атомарных операций для доступа.Или его можно использовать в обработчиках сигналов , где, поскольку поток может быть прерван, а обработчик запущен, а затем управление возвращено в том же потоке, вам необходимо использовать значение volatile
, если вы хотитепередать флаг прерванному коду.
Но если вы выполняете какую-либо синхронизацию между потоками, вы должны использовать блокировки или некоторые другие примитивы параллелизма, предоставляемые стандартной библиотекой, или действительно знать, что выделать в отношении упорядочения памяти и использовать барьеры памяти или атомарные операции.