Другие имеют обратился к «почему?». Легко, однако, свернуть свой собственный Add(ref double, double)
, используя CompareExchange
примитивно:
public static double Add(ref double location1, double value)
{
double newCurrentValue = location1; // non-volatile read, so may be stale
while (true)
{
double currentValue = newCurrentValue;
double newValue = currentValue + value;
newCurrentValue = Interlocked.CompareExchange(ref location1, newValue, currentValue);
if (newCurrentValue == currentValue)
return newValue;
}
}
CompareExchange
устанавливает значение location1
быть newValue
, если текущее значение равно currentValue
. Так как это делает атомным, потокобезопасным способом, мы можем полагаться только на него, не прибегая к блокировкам.
Почему цикл while (true)
? Подобные циклы являются стандартными при реализации оптимистически параллельных алгоритмов. CompareExchange
не изменится location1
, если текущее значение отличается от currentValue
. I был инициализирован currentValue
- location1
- выполнение долговременного чтения (которое может быть устаревшим, но это не меняет правильности, так как CompareExchange
проверит значение). Если текущее значение (неподвижное) - это то, что мы читали от location
, CompareExchange
изменит значение на newValue
. Если нет, мы должны повторить CompareExchange
с новым текущим значением, которое возвращается CompareExchange
.
Если значение изменилось на другой поток до момента следующего следующего CompareExchange
, оно снова потерпит неудачу, что потребует повторной попытки - и это теоретически может продолжаться вечно, отсюда и цикл. Если вы не постоянно меняете значение из нескольких потоков, CompareExchange
, скорее всего, будет вызываться только один раз, если текущее значение по-прежнему является тем, что нестабильное значение чтения location1
дало или два раза, если оно было другим.
Ссылка не активна. Вот резервная копия из интернет-архива. Https://web.archive.org/web/20160319061137/http://www.codemaestro.com/reviews/8 –