Здесь есть 2 в основном не связанных между собой предмета, которые всегда запутываются.
- летучий
- темы, блокировки, барьеры памяти и т. Д.
volatile используется для указания компилятору на создание кода для чтения переменной из памяти, а не из регистра. И чтобы не переупорядочивать код вокруг. В общем, не оптимизировать и не использовать «короткие пути».
Барьеры памяти (предоставляемые мьютексами, блокировками и т. Д.), Как цитировал Херб Саттер в другом ответе, предназначены для предотвращения CPU от переупорядочения запросов памяти на чтение / запись независимо от того, как компилятор сказал сделай это. т.е. не оптимизируйте, не используйте короткие пути - на уровне процессора.
Подобные, но на самом деле очень разные вещи.
В вашем случае, и в большинстве случаев блокировки, причина, по которой энергозависимость НЕ является необходимой, заключается в том, что вызовы функций делаются ради блокировки. то есть:
Обычные вызовы функций, влияющие на оптимизацию:
external void library_func(); // from some external library
global int x;
int f()
{
x = 2;
library_func();
return x; // x is reloaded because it may have changed
}
, если компилятор не может проверить library_func () и определить, что он не касается x, он будет перечитывать x при возврате. Это даже БЕЗ летучих.
Threading:
int f(SomeObject & obj)
{
int temp1;
int temp2;
int temp3;
int temp1 = obj.x;
lock(obj.mutex); // really should use RAII
temp2 = obj.x;
temp3 = obj.x;
unlock(obj.mutex);
return temp;
}
После прочтения obj.x для temp1, компилятор собирается перечитать obj.x для temp2 - НЕ из-за магии блокировок, а потому, что неясно, изменил ли lock () объект obj. Возможно, вы могли бы установить флаги компилятора для агрессивной оптимизации (без псевдонимов и т. Д.) И, таким образом, не перечитывать x, но тогда часть вашего кода, вероятно, начнет давать сбой.
Для temp3 компилятор (надеюсь) не будет перечитывать obj.x.
Если по какой-либо причине obj.x может измениться между temp2 и temp3, тогда вы будете использовать volatile (и ваша блокировка будет нарушена / бесполезна).
Наконец, если ваши функции lock () / unlock () были как-то встроены, возможно, компилятор мог бы оценить код и увидеть, что obj.x не изменился. Но я гарантирую одно из двух:
- встроенный код в конечном итоге вызывает некоторую функцию блокировки на уровне ОС (что препятствует оценке) или
- вы вызываете некоторые инструкции по защите памяти asm (то есть, которые заключены во встроенные функции, такие как __InterlockedCompareExchange), которые ваш компилятор распознает и, таким образом, избегает переупорядочения.
РЕДАКТИРОВАТЬ: P.S. Я забыл упомянуть - для вещей pthreads некоторые компиляторы помечены как «POSIX-совместимые», что означает, среди прочего, что они будут распознавать функции pthread_ и не будут делать плохих оптимизаций вокруг них. т. е. хотя в стандарте C ++ пока не упоминаются потоки, эти компиляторы (хотя бы минимально).
Итак, короткий ответ
вам не нужно летучих.