Joe Albahari имеет отличную серию для многопоточности, которая должна быть прочитана и должна быть известна наизусть для любого, кто выполняет многопоточность С#.
В части 4, однако, он упоминает проблемы с изменчивой:
Обратите внимание, что применение volatile не предотвращает запись, за которой следует читать от обмена, и это может создать головоломки. Джо Даффи хорошо иллюстрирует проблему в следующем примере: если Test1 и Test2 запускается одновременно на разных потоках, его возможно для a и b для обоих в конечном итоге значение 0 (несмотря на использование volatile on и x и y)
Далее следует примечание о том, что документация MSDN неверна:
В документации MSDN указано, что использование ключевого слова volatile обеспечивает что самое актуальное значение всегда присутствует в поле. Это неверно, поскольку, как мы видели, запись, за которой следует чтение, может переупорядочиваться.
Я проверил документацию MSDN, которая была в последний раз изменена в 2015 году, но по-прежнему перечисляет:
Ключевое слово volatile указывает, что поле может быть изменено несколько потоков, которые выполняются в одно и то же время. Поля, которые объявленные volatile не подпадают под оптимизацию компилятора, что допускать доступ одним потоком. Это гарантирует, что большинство текущее значение всегда присутствует в поле.
В настоящий момент я все же избегаю volatile в пользу более подробного, чтобы предотвратить потоки с использованием устаревших данных:
private int foo;
private object fooLock = new object();
public int Foo {
get { lock(fooLock) return foo; }
set { lock(fooLock) foo = value; }
}
Поскольку части о многопоточности были написаны в 2011 году, аргумент все еще действует сегодня? Следует ли избегать волатильности во что бы то ни стало в пользу блокировок или полных ограждений памяти, чтобы предотвратить очень сложное создание ошибок, которые, как уже упоминалось, даже зависят от поставщика ЦП, на котором он работает?