Здравствуйте, tdiff, Вы писали:
T>Возьмём классический пример синхронизации потоков:
{...}
T>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до
{...} T>Такая оптимизация возможна, т.к. компилятор видит, что раз внутри work() flag не меняется, поэтому T>нет смысла считывать его на каждой итерации.
T>Стандартное в практике решение этой проблемы — использовать mutex:
{...} T>Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать. T>Так ли это? Если да, то что это за инструкция на самом деле?
Насколько я знаю то что Вы написали в качестве "правильного" варианта тоже неверно. Обычно рекомендуется использовать специальные объекты ядра — события, семафоры, мьютексы (я проглядел по диагонали, по моемы Вы под мьютексом понимаете нечто другое). Когда Вы синхронизируетесь с помощью объекта ядра Ваш ждущий поток не крутится в цикле, поедая драгоценное время, а выключается из планировщика потоков и сразу же ставится в очередь как только объект сигнализирует о том, что ожидание закончилось.
То есть схема такая:
//Вы создаете событие в обоих синхронизируемых процессах.
HANDLE hEv=CreateEvent(NULL,FALSE,TRUE,"lalala");
//затем один процесс "засыпает" до окончания работы второго:
WaitForSingleObject(hEv);
//и делает что то дальше
//**********************************
//второй процесс чтото делает
{...}
//по окончании говорит, что момент пришел:
SetEvent(hEv) //именно в этот момент "проснется", то есть вернется из функции WaitForSingleObject первый процесс.
Компилятор, естественно, там ничего оптимизировать и испортить не сможет.