Возьмём классический пример синхронизации потоков:
bool flag = false;
void thread_1()
{
while (!flag) {
work();
}
}
void thread_2()
{
sleep(1000);
flag = true;
}
В функции work() flag не меняется.
Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до
void thread_1_real()
{
bool f = flag;
while (!f) {
work();
}
}
Такая оптимизация возможна, т.к. компилятор видит, что раз внутри work() flag не меняется, поэтому
нет смысла считывать его на каждой итерации.
Стандартное в практике решение этой проблемы — использовать mutex:
mutex m;
bool flag = false;
void thread_1_sync()
{
while (true) {
{
scoped_lock lock(m);
if (flag) break;
}
work();
}
}
void thread_2_sync()
{
sleep(1000);
{
scoped_lock lock(m);
flag = true;
}
}
Вопрос: что помешает компилятору и в этом случае оптимизировать чтение значения flag примерно вот так:
void thread_1_sync_real()
{
bool f;
scoped_lock lock(m) {
f = flag;
}
while (true) {
if (f) break;
work();
}
}
Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать.
Так ли это? Если да, то что это за инструкция на самом деле?