¿Para qué se utiliza Interlocked.CompareExchange en el método .net de dapper?

.net c# dapper thread-safety

Pregunta

En el código apuesto en el método Link.TryAdd , está el siguiente fragmento de código:

var snapshot = Interlocked.CompareExchange(ref head, null, null);

¿Por qué se requiere esto en lugar de simple?

var snapshot = head;

Ambas líneas no cambian el valor de la head , ambas líneas asignan el valor de la head a la snapshot . ¿Por qué el primero fue elegido sobre el segundo?

Editar: el código al que me refiero está aquí: https://github.com/SamSaffron/dapper-dot-net/blob/77227781c562e65c167bf7a933d69291d5bdc6f3/Dapper/SqlMapper.cs

Respuesta aceptada

Quieren hacer una lectura volátil, sin embargo, no hay una sobrecarga de Thread.VolatileRead que tome un parámetro de tipo genérico. El uso de Interlocked.CompareExchange esta manera logra el mismo resultado.

El problema que están tratando de resolver es que el compilador JIT puede optimizar la asignación a una temperatura temporal si lo considera oportuno. Esto puede causar problemas de subprocesamiento si otro subproceso muta la referencia principal mientras el subproceso actual lo está utilizando en una secuencia de operaciones.

Editar:

El problema no es que se lea un valor obsoleto al comienzo de TryAdd . El problema es que en la línea 105 necesitan comparar el encabezado actual con el encabezado anterior (contenido en snapshot ). Si hay una optimización, entonces no hay una variable de snapshot que tenga el valor anterior y la head se lee nuevamente en ese punto. Es muy probable que CompareExchange tenga éxito aunque la cabecera haya cambiado entre las líneas 103 y 105. El resultado es que un nodo en la lista se pierde si dos hilos llaman a TryAdd simultáneamente.


Respuesta popular

Mike z tiene razón: esto impide una optimización JIT (legal) que rompa el código.

Sin embargo, podrían haber usado el truco de la estructura volátil : Leer head y asignarlo a un campo volátil de alguna estructura. Luego, léelo de ese campo y se garantiza que será una lectura volátil.

La estructura en sí misma no importa en absoluto. Todo lo que importa es que se utilizó un campo volátil para copiar la variable.

Como eso:

struct VolatileHelper<T> { public volatile T Value; }
...
var volatileHelper = new VolatileHelper<Field>();
volatileHelper.Value = head;
var snapshot = volatileHelper.Value;

Con suerte, no tiene costo de tiempo de ejecución. En cualquier caso, el costo es menor que una operación interconectada que está causando tráfico de coherencia de memoria de la CPU.

En realidad, el hecho de que cada acceso de caché (incluso uno de lectura) requiere tráfico de coherencia de memoria hace que esto sea un caché lento . Las operaciones entrelazadas son un recurso global del sistema que no se escala con más CPU. Un acceso de enclavamiento utiliza un bloqueo de hardware global (por dirección de memoria, pero solo hay una dirección aquí).



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué