Глава.29 .Примитивные.конструкции.синхронизации.потоков
// Возвращаем максимальное значение, когда поток пытается его присвоить
return desiredVal;
}
Давайте посмотрим, что здесь происходит. В момент, когда метод начинает
выполняться, переменная
currentVal
инициализируется значением параметра
target
. Затем внутри цикла то же самое начальное значение получает переменная
startVal
. При помощи этой последней переменной вы можете выполнять любые
нужные вам операции. Они могут быть крайне сложными и состоять из тысяч
строк кода. Но в итоге должен быть сформирован результат, который помещается
в переменную
desiredVal
. В моем примере просто сравниваются переменные
startVal
и
value
.
Пока операция выполняется, значение
target
может быть изменено другим
потоком. Это маловероятно, но теоретически не исключено. Если это произойдет,
значение переменной
derivedVal
окажется основанным на старом значении пере-
менной
startVal
, а не на текущем значении параметра
target
, а следовательно,
мы не должны менять этот параметр. Гарантировать, что значение параметра
target
поменяется на значение переменной
desiredVal
при условии, что никакой
другой поток не поменяет его за спиной нашего потока, можно с помощью метода
Interlocked.CompareExchange
. Он проверяет, совпадает ли значение параметра
target
со значением переменной
startVal
(а именно его мы предполагаем у па-
раметра
target
перед началом выполнения операции). Если значение параметра
target
не поменялось, метод
CompareExchange
заменяет его новым значением
переменной
desiredVal
. Если же изменения произошли, метод
CompareExchange
не трогает параметр
target
.
Метод
CompareExchange
возвращает значение параметра
target
на момент
своего вызова, которое мы помещаем в переменную
currentVal
. Затем перемен-
ная
startVal
сравнивается с новым значением переменной
currentVal
. В случае
совпадения поток не меняет параметр
target
за нашей спиной, этот параметр
содержит значение переменной
desiredVal
, цикл
while
прекращает свою работу,
и метод возвращает управление. Если же значения не совпадают, значит, другой
поток поменял значение параметра
target
, поэтому параметру не было присвоено
значение переменной
desiredVal
, цикл переходит к следующей итерации и пробует
снова выполнить операцию на этот раз с новым значением переменной
currentVal
,
отражающей изменения, внесенные посторонним потоком.
Лично я использовал данный шаблон очень часто и даже создал инкапсулиро-
вавший его обобщенный метод
Morph
1
:
delegate Int32 Morpher(
Int32 startValue, TArgument argument,
out TResult morphResult);
1
Очевидно, что из-за метода обратного вызова morpher метод Morph хуже в плане про-
изводительности. Чтобы исправить ситуацию, сделайте код подставляемым (inline), как
в примере Maximum.
843
Конструкции.режима.ядра
static TResult Morph (
ref Int32 target, TArgument argument,
Morpher morpher) {
TResult morphResult;
Int32 currentVal = target, startVal, desiredVal;
do {
startVal = currentVal;
desiredVal = morpher(startVal, argument, out morphResult);
currentVal = Interlocked.CompareExchange(
ref target, desiredVal, startVal);
} while (startVal != currentVal);
return morphResult;
}
Do'stlaringiz bilan baham: |