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