La asignación de referencia es atómica, entonces, ¿por qué se necesita enclavarse.exchange (objeto ref, objeto)?

StackOverflow https://stackoverflow.com/questions/2192124

Pregunta

En mi servicio web ASMX multiproceso tenía un campo de clase _Alldata de mi propio tipo de sistema de sistema que consiste en pocos List<T> y Dictionary<T> marcado como volatile. Los datos del sistema (_allData) se actualiza de vez en cuando y lo hago creando otro objeto llamado newData y llene sus estructuras de datos con nuevos datos. Cuando termina, solo asigno

private static volatile SystemData _allData

public static bool LoadAllSystemData()
{
    SystemData newData = new SystemData();
    /* fill newData with up-to-date data*/
     ...
    _allData = newData.
} 

Esto debería funcionar ya que la asignación es atómica y los subprocesos que tienen la referencia a los datos antiguos lo siguen utilizando y el resto tienen los nuevos datos del sistema justo después de la asignación. Sin embargo, mi collegue dijo que en lugar de usar volatile Palabra clave y asignación simple que debería usar InterLocked.Exchange Porque dijo que en algunas plataformas no se garantiza que la asignación de referencia sea atómica. Además: cuando declaro the _allData campo volatile la

Interlocked.Exchange<SystemData>(ref _allData, newData); 

Produce advertencia "Una referencia a un campo volátil no será tratado como volátil" ¿Qué debería pensar sobre esto?

¿Fue útil?

Solución

Hay numerosas preguntas aquí. Considerándolos uno a la vez:

La asignación de referencia es atómica, entonces, ¿por qué se necesita enclavarse.exchange (objeto ref, objeto)?

La asignación de referencia es atómica. Interlocked.Exchange no hace solo asignación de referencia. Hace una lectura del valor actual de una variable, almacena el valor anterior y asigna el nuevo valor a la variable, todo como operación atómica.

Mi colega dijo que en algunas plataformas no está garantizada que la asignación de referencia sea atómica. ¿Estaba mi colega correcto?

No. Se garantiza que la asignación de referencia es atómica en todas las plataformas .NET.

Mi colega es razonamiento de las instalaciones falsas. ¿Eso significa que sus conclusiones son incorrectas?

No necesariamente. Su colega podría estar dando buenos consejos por malas razones. Quizás haya alguna otra razón por la que deba estar usando entrelazado.exchange. La programación sin bloqueo es increíblemente difícil y en el momento en que se aparta de las prácticas bien establecidas adoptadas por expertos en el campo, está en las malas hierbas y arriesga el peor tipo de condiciones de carrera. No soy un experto en este campo ni un experto en su código, por lo que no puedo juzgar una forma u otra.

Produce advertencia "Una referencia a un campo volátil no será tratado como volátil" ¿Qué debería pensar sobre esto?

Debe entender por qué esto es un problema en general. Eso conducirá a una comprensión de por qué la advertencia no es importante en este caso particular.

La razón por la que el compilador brinda esta advertencia es porque marcar un campo como volátil significa "este campo se actualizará en múltiples hilos: no genere ningún código que almacene valores de este campo y asegúrese de que se lean o escribas de Este campo no se "trasladan hacia adelante y hacia atrás en el tiempo" a través de inconsistencias de caché del procesador ".

(Supongo que ya entiendes todo eso. Si no tienes una comprensión detallada del significado de volátil y cómo afecta la semántica de caché del procesador, entonces no entiende cómo funciona y no debe usar volátiles. Programas sin bloqueo son muy difíciles de hacer bien; asegúrese de que su programa sea correcto porque comprende cómo funciona, no por accidente).

Ahora suponga que hace una variable que es un alias de un campo volátil al pasar una árbitro a ese campo. ¡Dentro del método llamado, el compilador no tiene ninguna razón para saber que la referencia debe tener una semántica volátil! El compilador generará alegremente el código para el método que no implementa las reglas para los campos volátiles, pero la variable es un campo volátil. Eso puede destruir por completo su lógica sin bloqueo; La suposición es siempre que un campo volátil es siempre accedido con semántica volátil. No tiene sentido tratarlo como volátil a veces y no otras veces; tienes que siempre Sea consistente de lo contrario, no puede garantizar la consistencia en otros accesos.

Por lo tanto, el compilador advierte cuándo haces esto, porque probablemente arruinará completamente tu lógica sin bloqueo cuidadosamente desarrollada.

Por supuesto, entrelazado.exchange es Escrito para esperar un campo volátil y hacer lo correcto. Por lo tanto, la advertencia es engañosa. Lamento mucho esto; Lo que deberíamos haber hecho es implementar algún mecanismo por el cual un autor de un método como Interlocked.Exchange podría poner un atributo en el método que dice "Este método que toma una reformulación de semántica volátil en la variable, por lo que suprime la advertencia". Quizás en una versión futura del compilador lo haremos.

Otros consejos

O su Collegue está equivocado o sabe algo que la especificación del idioma C# no.

5.5 Atomicidad de referencias variables:

"Las lecturas y las escrituras de los siguientes tipos de datos son atómicas: bool, char, byte, sbyte, short, ushort, uint, int, float y tipos de referencia".

Por lo tanto, puede escribir en la referencia volátil sin riesgo de obtener un valor corrupto.

Por supuesto, debe tener cuidado con cómo decide qué hilo debe obtener los nuevos datos, para minimizar el riesgo de que más de un hilo a la vez lo haga.

Interlocked.Exchange <T>

Establece una variable del tipo T especificado en un valor especificado y devuelve el valor original, como una operación atómica.

Cambia y devuelve el valor original, es inútil porque solo quieres cambiarlo y, como dijo Gffa, ya es atómico.

A menos que un perfilador sea probado que sea un cuello de botella en su aplicación, debe considerar bloqueos sinceros, es más fácil entender y demostrar que su código es correcto.

Iterlocked.Exchange() no es solo atómico, también se encarga de la visibilidad de la memoria:

Las siguientes funciones de sincronización utilizan las barreras apropiadas para garantizar el orden de la memoria:

Funciones que ingresan o dejan secciones críticas

Funciones que señalan objetos de sincronización

Funciones de espera

Funciones entrelazadas

Sincronización y problemas del multiprocesador

Esto significa que, además de la atomicidad, asegura que:

  • Para el hilo llamándolo:
    • No se realiza el reordenamiento de las instrucciones (por el compilador, el tiempo de ejecución o el hardware).
  • Para todos los hilos:
    • No se lee a la memoria que ocurra antes de esta instrucción verá el cambio que realiza esta instrucción.
    • Todas las lecturas después de esta instrucción verán el cambio realizado por esta instrucción.
    • Todas las escrituras en la memoria después de que esta instrucción ocurra después de que este cambio de instrucción haya alcanzado la memoria principal (al enjuagar este cambio de instrucción a la memoria principal cuando esté listo y no dejar que el hardware se enjuague en el tiempo).
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top