Otros tienen se dirigió al "¿por qué?". Es fácil, sin embargo a rodar su propia Add(ref double, double)
, utilizando la primitiva 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
establece el valor de location1
ser newValue
, si el valor actual es igual currentValue
. Como lo hace de una manera atómica y segura para hilos, podemos confiar solo en ella sin recurrir a bloqueos.
¿Por qué el lazo while (true)
? Los bucles como este son estándar cuando se implementan algoritmos concurrentemente optimistas. CompareExchange
no cambiará location1
si el valor actual es diferente de currentValue
. Inicialicé currentValue
a location1
- haciendo una lectura no volátil (que puede ser obsoleta, pero eso no cambia la corrección, ya que CompareExchange
comprobará el valor). Si el valor actual (fijo) es el que hemos leído en location
, CompareExchange
cambiará el valor a newValue
. Si no, tenemos que volver a intentar CompareExchange
con el nuevo valor actual, como se devuelve por CompareExchange
.
Si el valor se cambia por otro hilo hasta el momento de nuestro próximo CompareExchange
nuevamente, volverá a fallar, necesitando otro reintento, y esto puede continuar en teoría para siempre, de ahí el ciclo. A menos que cambie constantemente el valor de varios hilos, lo más probable es que llame al CompareExchange
solo una vez, si el valor actual sigue siendo el que arrojó la lectura no volátil de location1
, o dos veces, si fue diferente.
El enlace ya no está activo. Aquí hay una copia de seguridad de Internet Archive https://web.archive.org/web/20160319061137/http://www.codemaestro.com/reviews/8 –