Ответ 1
Например, предположим, что у нас есть два потока, поток A, который читает только из общей переменной и потока B, который только записывает в общую переменную. Правильная синхронизация, например. mutexs pthreads принудительно.
IIUC, без ключевого слова volatile, компилятор может посмотреть код потока A и сказать: "Здесь переменная не изменяется, но мы имеем много чтений, давайте ее читаем только один раз, кешируем значение и оптимизируйте все последующие чтения". Также он может посмотреть код потока B и сказать: "У нас здесь много записей этой переменной, но не читается, поэтому письменные значения не нужны и, следовательно, позволяют оптимизировать все записи".
Как и большинство примитивов синхронизации потоков, операции mutex pthreads имеют явно определенную семантику видимости памяти.
Либо платформа поддерживает pthreads, либо нет. Если он поддерживает pthreads, он поддерживает мьютексы pthreads. Либо эти оптимизации безопасны, либо нет. Если они в безопасности, проблем нет. Если они небезопасны, любая платформа, которая их делает, не поддерживает мьютексы pthreads.
Например, вы говорите: "Здесь переменная не изменяется, но она делает - другой поток может ее изменить. Если компилятор не может доказать, что его оптимизация не может нарушить какую-либо соответствующую программу, она не сможет это сделать. И соответствующая программа может модифицировать переменную в другом потоке. Либо компилятор поддерживает потоки POSIX, либо нет.
Как это бывает, большинство из них происходит автоматически на большинстве платформ. Компилятору просто не дают понять, что делают операции мьютекса внутри. Все, что мог сделать другой поток, могут выполнять сами операции mutex. Поэтому компилятор должен "синхронизировать" память перед входом и выходом из этих функций. Например, он не может хранить значение в регистре по вызову pthread_mutex_lock
, потому что для всего, что он знает, pthread_mutex_lock
обращается к этому значению в памяти. В качестве альтернативы, если компилятор имеет специальные знания о функциях мьютекса, это будет включать в себя знание о недействительности значений кеширования, доступных для других потоков в этих вызовах.
Платформа, требующая volatile
, будет в значительной степени непригодна. Вам понадобятся версии каждой функции или класса для конкретных случаев, когда объект может быть видимым или видимым из другого потока. Во многих случаях вам просто нужно сделать все volatile
, а не кешировать значения в регистрах, это не стартер производительности.
Как вы, наверное, слышали много раз, семантика volatile
, как указано на языке C, просто не смешивается с потоками. Мало того, что этого недостаточно, он отключает многие совершенно безопасные и практически необходимые оптимизации.