2010-01-22 13 views

Respuesta

13

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.

+0

+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

+0

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. –

12

En aplicaciones multiproceso, hay muchas situaciones donde el acceso simultáneo a los mismos datos puede causar problemas. En tales casos, se requiere sincronización para garantizar que solo un hilo tenga acceso en cualquier momento.

Imagino que significan usar el lock-statement (o SyncLock en VB.NET) contra el uso de Monitor.

Es posible que desee read this page para obtener ejemplos y una comprensión del concepto. Sin embargo, si no tiene experiencia con el diseño de aplicaciones multiproceso, probablemente se volverá rápidamente evidente, en caso de que su nuevo empleador lo ponga a prueba. Es un tema bastante complicado, con muchas trampas posibles, como deadlock.

También hay un MSDN page on the subject decente.

Puede haber otras opciones, dependiendo del tipo de variable de miembro y cómo se va a cambiar. Incrementar un entero, por ejemplo, se puede hacer con el método Interlocked .Increment.

Como ejercicio y demostración del problema, intente escribir una aplicación que inicie 5 subprocesos simultáneos, incrementando un contador compartido un millón de veces por subproceso. El resultado final previsto del contador sería de 5 millones, pero eso es (probablemente) no lo que va a terminar :)

Editar: hice una implementación rápida yo mismo (download). Salida de muestra:

Unsynchronized counter demo: 
expected counter = 5000000 
actual counter = 4901600 
Time taken (ms) = 67 

Synchronized counter demo: 
expected counter = 5000000 
actual counter = 5000000 
Time taken (ms) = 287 
+0

Gracias por la respuesta. Me gustaría saber más acerca de multi threading en .NET (C#). De hecho, necesito saber más sobre C# en general. ¿Me podría sugerir un libro que puede transformar un desarrollador ordinario en desarrollador de próxima generación? – Supremestar

+0

No me vienen a la mente títulos específicos, pero no uso mucho los libros. De hecho, es bastante sorprendente que pre-ordenara C# en profundidad, segunda edición. Buenas cosas, pero probablemente haya libros más adecuados a sus necesidades. – Thorarin

+2

"C# en profundidad" es un libro increíble. –

0

Hay un par de formas, varias de las cuales se mencionaron anteriormente.

  1. ReaderWriterLockSlim es mi método preferido. Esto le da un tipo de base de bloqueo y permite la actualización (aunque la sintaxis de eso es incorrecta en MSDN la última vez que lo busqué y no es muy obvio)
  2. declaraciones de bloqueo. Tratas una lectura como una escritura y simplemente impides el acceso a la variable
  3. Operaciones enclavadas. Esto realiza operaciones en un tipo de valor en un paso atómico. Esto puede ser usado para el roscado libre de bloqueo (realmente no lo recomendaría)
  4. mutex y semáforos (no han utilizado estos)
  5. declaraciones del monitor (se trata fundamentalmente de cómo funciona la palabra clave de bloqueo)

Si bien no pretendo denigrar otras respuestas, no confiaría en nada que no use una de estas técnicas. Mis disculpas si las he olvidado.

Cuestiones relacionadas