Давайте просто скажем, что у вас простая операция, выполняемая в фоновом потоке. Вы хотите предоставить способ отменить эту операцию, чтобы создать флагов boolean, который вы установили в true из обработчика события клика кнопки отмены.
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 как изменчивую. Это необходимо?
[Я знаю, что есть класс BackgroundWorker с его встроенным методом CancelAsync(), но меня интересует семантика и использование доступа к блокировке и потоковой переменной здесь, а не конкретная реализация, код - всего лишь пример.]
Кажется, есть две теории.
1) Поскольку это простой встроенный тип (и доступ к встроенным типам является атомарным в .net), и поскольку мы пишем только в одном месте и только чтение в фоновом потоке, нет необходимости блокировать или отмечать как изменчивый.
2) Вы должны пометить его как volatile, потому что если вы не компилятор, он может оптимизировать чтение в цикле while, потому что он не считает, что он способен изменять значение.
Какая правильная техника? (И почему?)
[Редактировать: На этом, кажется, две четко определенные и противоположные школы мысли. Я ищу окончательный ответ на это, поэтому, пожалуйста, по возможности укажите свои причины и укажите источники вместе с вашим ответом.]