Широко распространено мнение, что ключевое слово volatile хорошо для многопоточного программирования.
Ханс Боэм указывает , что существует только три портативных варианта использования летучих:
- volatile может использоваться для маркировки локальных переменных в той же области видимости, что и setjmp, значение которого должно сохраняться в longjmp. Неясно, какая доля таких видов использования будет замедлена, поскольку ограничения атомарности и порядка не действуют, если нет возможности совместно использовать данную локальную переменную. (Даже неясно, какая доля такого использования будет замедлена, если все переменные будут сохранены в длинном диапазоне, но это отдельный вопрос, который здесь не рассматривается.)
- volatile может использоваться, когда переменные могут быть «внешне модифицированы», но изменение фактически инициируется синхронно самим потоком, например, потому что основная память отображается в нескольких местах.
- A volatile sigatomic_t может использоваться для связи с обработчиком сигнала в том же потоке ограниченным образом. Можно подумать об ослаблении требований к случаю sigatomic_t, но это выглядит довольно нелогичным.
Если вы многопоточны ради скорости, замедление кода определенно не то, что вам нужно. Для многопоточного программирования есть две ключевые проблемы, которые часто ошибочно считают изменчивыми:
- валентность
- согласованность памяти , то есть порядок операций потока, видимый другим потоком.
Давайте сначала разберемся с (1). Volatile не гарантирует атомарного чтения или записи. Например, энергозависимое чтение или запись 129-битной структуры не будет атомарным на большинстве современных аппаратных средств. Изменчивое чтение или запись 32-битного int является атомарным на большинстве современного оборудования, но volatile не имеет к этому никакого отношения . Скорее всего, он будет атомным без летучих. Атомность находится в прихоти компилятора. В стандартах C или C ++ нет ничего, что говорило бы, что оно должно быть атомарным.
Теперь рассмотрим вопрос (2). Иногда программисты думают о volatile как об отключении оптимизации энергозависимого доступа. Это в значительной степени верно на практике. Но это только волатильные доступы, а не энергонезависимые. Рассмотрим этот фрагмент:
volatile int Ready;
int Message[100];
void foo( int i ) {
Message[i/10] = 42;
Ready = 1;
}
Он пытается сделать что-то очень разумное в многопоточном программировании: написать сообщение, а затем отправить его в другой поток. Другой поток будет ждать, пока Ready не станет ненулевым, и затем прочитает сообщение. Попробуйте скомпилировать это с помощью "gcc -O2 -S", используя gcc 4.0 или icc. Оба сначала сохранят данные в Ready, так что они могут перекрываться с вычислением i / 10. Изменение порядка не является ошибкой компилятора. Это агрессивный оптимизатор, выполняющий свою работу.
Вы можете подумать, что решение состоит в том, чтобы пометить все ссылки на память как изменчивые. Это просто глупо. Как говорят предыдущие цитаты, это просто замедлит ваш код. Что еще хуже, это может не решить проблему. Даже если компилятор не переупорядочивает ссылки, аппаратное обеспечение может. В этом примере аппаратное обеспечение x86 не будет переупорядочивать его. Так же как и процессор Itanium (TM), поскольку компиляторы Itanium вставляют ограждения памяти для энергозависимых хранилищ. Это умное расширение Itanium. Но чипы вроде Power (TM) будут переупорядочены. Для заказа вам действительно понадобятся ограждения памяти , также называемые барьеры памяти . Ограничитель памяти предотвращает изменение порядка операций с памятью через ограничитель или, в некоторых случаях, предотвращает изменение порядка следования в одном направлении. Volatile не имеет ничего общего с ограничителями памяти.
Так, каково решение для многопоточного программирования? Используйте расширение библиотеки или языка, которое реализует атомарную семантику и семантику заборов. При использовании по назначению, операции в библиотеке вставят правильные заборы. Некоторые примеры:
- POSIX темы
- Windows (TM) темы
- OpenMP
- TBB
Основано на статье Арка Робисона (Intel)