Возможно, я пишу пистолет здесь, но это звучит для меня, как будто вы путаете здесь два вопроса.
Один из них - атомарность, которая, по моему мнению, означает, что одна операция (которая может потребовать нескольких шагов) не должна вступать в конфликт с другой такой отдельной операцией.
Другое - волатильность, когда ожидается, что это значение изменится и почему.
Возьмите первый. Если ваша двухэтапная операция требует, чтобы вы прочитали текущее значение, измените его и запишите обратно, вам наверняка захочется блокировка, если только эта операция не может быть переведена в одну инструкцию CPU, которая может работать на одиночная кэш-строка данных.
Однако вторая проблема заключается в том, что даже когда вы делаете блокировку, что видят другие потоки.
A volatile
Поле в .NET - это поле, которое знает компилятор, в любое время. В однопоточном мире изменение переменной - это нечто, что происходит в какой-то момент в последовательном потоке инструкций, поэтому компилятор знает, когда он добавил код, который его изменяет, или, по крайней мере, когда он вызвал внешний мир, что может или не может быть изменено так, что, как только код вернется, это может быть не то же самое значение, которое было до вызова.
Это знание позволяет компилятору поднять значение из поля в регистр один раз, до цикла или аналогичного блока кода и никогда не перечитывать значение из поля для этого конкретного кода.
С многопоточным, однако, это может дать вам некоторые проблемы. Один поток мог бы скорректировать значение, а другой поток из-за оптимизации не будет читать это значение в течение некоторого времени, потому что он знает, он не изменился.
Итак, когда вы указываете поле как volatile
, вы в основном говорите компилятору, что он не должен предполагать, что он имеет текущее значение этого в любой точке, за исключением захвата снимков каждый раз, когда ему нужно значение.
Замки решают многошаговые операции, волатильность обрабатывает то, как компилятор кэширует значение поля в регистре, и вместе они будут решать больше проблем.
Также обратите внимание, что если в поле содержится что-то, что не может быть прочитано в одной команде cpu, вам, скорее всего, захочется также заблокировать доступ для чтения.
Например, если вы используете 32-разрядный процессор и записываете 64-битное значение, для операции записи потребуется выполнить два шага, и если другому потоку на другом процессоре удастся прочитать 64-битный значение до того, как шаг 2 завершится, он получит половину предыдущего значения и половину нового, красиво смешанного вместе, что может быть даже хуже, чем получение устаревшего.
Редактировать: Чтобы ответить на комментарий, что volatile
гарантирует атомарность операции чтения/записи, что это хорошо, правда, в некотором смысле, потому что volatile
ключевое слово не может быть применено к полям, которые больше 32-разрядный, в действительности, делая полевую командную инструкцию с одним процессором для чтения и записи как на 32, так и на 64-битных процессорах.И да, это предотвратит внесение значения в регистр как можно больше.
Так что часть комментария неверна, volatile
не может быть применена к 64-битным значениям.
Следует также отметить, что volatile
имеет некоторую семантику относительно переупорядочения операций чтения/записи.
Для получения соответствующей информации см. MSDN documentation или C# specification, найдено here, раздел 10.5.3.
Это * очень хорошее чтение (и, безусловно, заслуживает +1), но обычно предпочитают, чтобы ответы SO были несколько самодостаточными. Было бы неплохо, если бы ваш ответ содержал краткое изложение важных частей этой статьи, так что (1) люди, просматривающие страницу, увидят это, и (2), поэтому ответ остается актуальным, если статья перемещена или снята. – jalf