2011-02-23 9 views
11

Tengo un código de varios subprocesos. Me gustaría aumentar el rendimiento un poco, así que me pregunto si puedo deshacerme de un bloqueo..NET Enhebrado: es necesario un bloqueo para las asignaciones

Tengo un miembro de campo:

private IList<ServerStatus> status; 

Se actualiza en un hilo de este modo:

status = GetUpdatedStatus(); 

y se usa en otro hilo de esta manera:

var currentStatus = status; 

Entonces, la pregunta es, ¿puede lo anterior producir algún problema sin bloqueos alrededor de las dos declaraciones de asignación?

supongo que el único escenario que puedo ver es currentStatus siendo nula, pero entonces, yo era de esperar una asignación a ser algo seguro para subprocesos (ya sea que se ha cambiado la referencia o no)

Respuesta

20

Tiene usted razón. Verás la tarea o no la verás. Las asignaciones (y lecturas) de referencias son siempre "atómicas" (al final es porque en máquinas de 32 bits las referencias son de 32 bits, por lo que se puede hacer atómicamente, y en máquinas de 64 bits (ejecutando una aplicación de 64 bits) las referencias son 64 bits; por lo que se puede hacer de forma atómica. La única excepción está intentando escribir/leer unas largas (64 bits) en una máquina de 32 bits. Hay que tendría que utilizar Interlocked.Read/Interlocked.Exchange)

Normalmente debería declarar estado como volatile, por lo que cada subproceso solo ve la última versión. Deberías leer esto: http://www.albahari.com/threading/ ¡es muy, muy bueno!

Si usted no confía en mí, lea la sección Do We Really Need Locks and Barriers? aquí http://www.albahari.com/threading/part4.aspx

Ah ... me olvidaba ... el mundo los odia, así que hay una pequeña cosa que saber de volátiles: a veces no funciona :-) :-) Lea, en la misma página del otro ejemplo, la sección The volatile keyword, la parte BAJO el cuadro rojo. Notice that applying volatile doesn’t prevent a write followed by a read from being swapped, and this can create brainteasers. Al final, la única forma de estar seguro es usar Interlocked.Exchange para escribir y Interlocked.CompareExchange para leer algo O proteger las secciones de lectura y escritura con sincronización (como lock) O llenar su programa con Thread.MemoryBarrier (pero no lo intente) , fallarás, y ni siquiera sabrás por qué). Se le garantiza que todas las lecturas y escrituras hechas en el bloqueo se realizarán en el bloqueo, no antes ni después.

+0

excelente respuesta, y gracias por los enlaces - He leído un poco sobre volátil que no trabaja al 100% antes, por lo que tienden a permanecer lejos de él y usa barreras de memoria o bloqueo en su lugar. – Steffen

+1

Tuve que trabajar con hilos, sincronizaciones, etc. el mes pasado, así que leí casi todo lo que había en Internet sobre el argumento. Enhebrar puede dar pesadillas si desea sincronizar estrechamente los hilos. – xanatos

6

escrituras de referencia están garantizados atómica, por lo que sólo hay realmente dos cosas a comprobar:

uso
  • en un bucle estrecho - podría necesitar agregar volatile si es necesario notar el cambio
  • actualizaciones dobles; si está (por ejemplo) haciendo add/remove mediante swap de referencia, debe usar Interlocked.CompareExchange para asegurarse de no perder datos; mantener a volver a aplicar los cambios hasta que gane el canje

es decir

object snapshot, newValue; 
do 
{ 
    snapshot = field; 

    // do something based on that; create a clone 
    // with more/less data for example 
    newValue = ...; 
} while (!ReferenceEquals(
    Interlocked.CompareExchange(ref field, newValue, snapshot), snapshot)); 
+0

Marc Ya te leí diciendo eso en SO y realmente me estoy preguntando el propósito de esta técnica. Usted dice "hasta que gane el intercambio", pero si dos hilos están ejecutando su ciclo, ¿cómo termina? ¿Cómo puede uno ganarlo? No lo entiendo Si otro subproceso busca los datos mientras tanto, por qué no lo nota con CompareExchange y utiliza estos datos en lugar del de su hilo. Sé que es la forma opuesta de ver las cosas, pero no entiendo cómo este código puede funcionar en varios hilos que lo ejecuten. Gracias. – Nock

+0

El intercambio comparado de Nock realiza un intercambio atómico si el valor aún coincide; en el caso de la competencia, se garantiza que al menos un hilo será exitoso por iteración, si no se logra ningún cambio, en cuyo caso no todos pueden ser infructuosos (contradicción) –

+0

Bien ahora entiendo, si hay 10 hilos compiten, luego un hilo "es correcto" por iteración (luego sale) y tomaría 10 iteraciones para que el último hilo sea exitoso. Una última pregunta: ¿cuál es el sentido de esto? ¿Qué beneficio tiene en comparación con una herramienta en bruto que no se molestaría en saber si la escritura se cometió o no? Quiero decir, si su bucle no tiene una lógica adicional para cada iteración que detecta que no pudo confirmar y adaptarse a esta falla, vuelva a intentarlo. Para ser más claro: el punto es saber que fallaste (y reaccionar en consecuencia) o que te asegures de comprometerte? (si es así, ¿por qué?) – Nock

Cuestiones relacionadas