Al cambiar los datos en C#, algo que se ve como una sola operación se puede compilar en varias instrucciones. Tomar la clase siguiente:
public class Number {
private int a = 0;
public void Add(int b) {
a += b;
}
}
Cuando se construye, se obtiene el siguiente código IL:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: dup
// Pushes the value of the private variable 'a' onto the stack
IL_0003: ldfld int32 Simple.Number::a
// Pushes the value of the argument 'b' onto the stack
IL_0008: ldarg.1
// Adds the top two values of the stack together
IL_0009: add
// Sets 'a' to the value on top of the stack
IL_000a: stfld int32 Simple.Number::a
IL_000f: ret
Ahora, supongamos que tiene un Number
objeto y dos hilos de llamar a su método Add
así:
number.Add(2); // Thread 1
number.Add(3); // Thread 2
Si quiere que el resultado sea 5 (0 + 2 + 3), hay un problema. No sabes cuándo estos subprocesos ejecutarán sus instrucciones. Ambos hilos podrían ejecutar IL_0003
(empujando a cero en la pila) antes de que cualquiera ejecuta IL_000a
(en realidad, el cambio de la variable de miembro) y se obtiene lo siguiente:
a = 0 + 2; // Thread 1
a = 0 + 3; // Thread 2
El último hilo para terminar 'victorias' y al final del proceso , a
es 2 o 3 en lugar de 5.
Por lo tanto, debe asegurarse de que un conjunto completo de instrucciones termine antes del otro conjunto.Para hacer eso, usted puede:
1) Bloquear acceso al miembro de la clase mientras que está siendo escrito, utilizando uno de los muchos .NET synchronization primitives (como lock
, Mutex
, ReaderWriterLockSlim
, etc.), de modo que sólo un hilo puede trabajar en él a la vez
2) Empuje las operaciones de escritura en una cola y procese esa cola con un solo hilo. Como señala Thorarin, aún tiene que sincronizar el acceso a la cola si no es seguro para subprocesos, pero vale la pena para operaciones de escritura complejas.
Existen otras técnicas. Algunos (como Interlocked
) están limitados a tipos de datos particulares, y hay incluso más (como los que se analizan en Non-blocking synchronization y Part 4 of Joseph Albahari's Threading in C#), aunque son más complejos: acérquelos con precaución.
+1 para ReadWriterLockSlim. Sin embargo, creo que el OP necesita familiarizarse con la programación de subprocesos múltiples antes de captar ese concepto por completo. En cuanto a su segundo punto: necesitaría sincronizar la cola, simplemente complicando su problema. – Thorarin
Cierto, y tu respuesta proporciona una introducción muy superior a esos conceptos (aunque estoy tratando de tomar una táctica diferente en una actualización). Acerca de la cola, es cierto que tiene que estar sincronizada también, aunque hay casos en que ese enfoque simplifica las cosas en lugar de complicarlas. –