Нужно ли блокировать или помечать как энергозависимые при доступе к простому логическому флагу в C #?
Допустим, у вас есть простая операция, которая выполняется в фоновом потоке. Вы хотите предоставить способ отменить эту операцию, чтобы создать логический флаг, для которого вы установили значение true из обработчика события click кнопки отмены.
private bool _cancelled;
private void CancelButton_Click(Object sender ClickEventArgs e)
{
_cancelled = true;
}
Теперь вы устанавливаете флаг отмены из потока графического интерфейса, но вы читаете его из фонового потока. Вам нужно заблокировать перед доступом к bool?
Вам нужно сделать это (и, очевидно, заблокировать обработчик события нажатия кнопки):
while(operationNotComplete)
{
// Do complex operation
lock(_lockObject)
{
if(_cancelled)
{
break;
}
}
}
Или это допустимо (без блокировки):
while(!_cancelled & operationNotComplete)
{
// Do complex operation
}
Или как насчет пометки переменной _cancelled как volatile. Это необходимо?
[Я знаю, что есть класс BackgroundWorker со встроенным в него методом CancelAsync (), но я заинтересован в семантике и использовании блокировки и доступа к переменным потокам здесь, а не в конкретной реализации, код является лишь примером.]
Кажется, есть две теории.
1) Поскольку это простой встроенный тип (а доступ к встроенным типам является атомарным в .net), и поскольку мы пишем в него только в одном месте и читаем только в фоновом потоке, нет необходимости блокировать или помечать как энергозависимые.
2) Вы должны пометить его как volatile, потому что, если вы не сделаете этого, компилятор может оптимизировать чтение в цикле while, потому что он ничего не думает, что он способен изменить значение.
Какая техника правильная? (И почему?)
[Редактировать: Кажется, есть две четко определенные и противоположные точки зрения по этому вопросу. Я ищу окончательный ответ на этот вопрос, поэтому, если возможно, опубликуйте свои причины и приведите источники вместе с ответом.]