validar traves subprocesos seguras realizar para otro obligatorios metodo llamar llamadas formularios formulario desde controles control como checkforillegalcrossthreadcalls campos acceder c# .net multithreading locking

traves - validar campos obligatorios en c#



Necesitamos bloquear un.NET Int32 cuando lo leemos en un código multiproceso? (6)

Depende exactamente cómo va a utilizar el número de 32 bits.

Si desea realizar una operación como:

i++;

Eso se divide implícitamente en

  1. leyendo el valor de i
  2. agregando uno
  3. almacenando i

Si otro hilo modifica i después de 1, pero antes de 3, entonces tienes un problema donde yo tenía 7 años, le agregas uno y ahora es 492.

Pero si simplemente está leyendo i o realizando una sola operación, como:

i = 8;

entonces no necesitas bloquear i.

Ahora, su pregunta dice, "... necesita bloquear un .NET Int32 cuando lo lee ..." pero su ejemplo implica leer y luego escribir en un Int32.

Entonces, depende de lo que estés haciendo.

Estaba leyendo el siguiente artículo: http://msdn.microsoft.com/en-us/magazine/cc817398.aspx "Solución de 11 problemas probables en su código multiproceso" por Joe Duffy

Y me planteó una pregunta: "¿Necesitamos bloquear un .NET Int32 cuando lo leemos en un código multiproceso?"

Entiendo que si se tratara de un Int64 en un SO de 32 bits podría desgarrarse, como se explica en el artículo. Pero para Int32 imaginé la siguiente situación:

class Test { private int example = 0; private Object thisLock = new Object(); public void Add(int another) { lock(thisLock) { example += another; } } public int Read() { return example; } }

No veo una razón para incluir un bloqueo en el método Read. ¿Vos si?

Actualización Basado en las respuestas (por Jon Skeet y ctacke) Entiendo que el código anterior sigue siendo vulnerable al almacenamiento en caché de multiprocesador (cada procesador tiene su propio caché, no sincronizado con otros). Las tres modificaciones siguientes resuelven el problema:

  1. Agregar a "ejemplo int" la propiedad "volátil"
  2. Insertar un Thread.MemoryBarrier (); antes de la lectura real de "ejemplo int"
  3. Lea "int example" dentro de "lock (thisLock)"

Y también creo que "volátil" es la solución más elegante.


El bloqueo es necesario si necesita que sea atómico. Leer y escribir (como una operación emparejada, como cuando haces un i ++) no se garantiza que un número de 32 bits sea atómico debido al almacenamiento en caché. Además, una lectura o escritura individual no necesariamente va directamente a un registro (volatilidad). Hacerlo volátil no le da ninguna garantía de atomicidad si tiene el deseo de modificar el número entero (por ejemplo, una operación de lectura, incremento, escritura). Para enteros, un mutex o monitor puede ser demasiado pesado (depende de su caso de uso) y para eso es la clase Interlocked . Garantiza la atomicidad de estos tipos de operación.


Locking logra dos cosas:

  • Actúa como un mutex, por lo que puede asegurarse de que solo un hilo modifique un conjunto de valores a la vez.
  • Proporciona barreras de memoria (semántica de adquisición / liberación) que garantiza que las escrituras de memoria creadas por un hilo sean visibles en otro.

La mayoría de las personas entiende el primer punto, pero no el segundo. Supongamos que utiliza el código en la pregunta de dos subprocesos diferentes, con un subproceso que invoca Add repetidamente y otro subproceso que invoca a Read . La atomicidad por sí misma aseguraría que solo terminaras leyendo un múltiplo de 8, y si hubiera dos hilos llamando a Add tu cerradura, te asegurarías de que no "perdieras" ninguna adición. Sin embargo, es muy posible que su hilo de Read solo lea 0, incluso después de haber llamado varias veces. Sin barreras de memoria, el JIT podría simplemente almacenar en caché el valor en un registro y asumir que no había cambiado entre lecturas. El objetivo de una barrera de memoria es asegurarse de que algo esté realmente escrito en la memoria principal o realmente leído desde la memoria principal.

Los modelos de memoria pueden ponerse bastante peludos, pero si sigues la simple regla de sacar un candado cada vez que quieres acceder a datos compartidos (para leer o escribir) estarás bien. Consulte la parte de volatilidad / atomicidad de mi tutorial de subprocesos para obtener más detalles.


Tener solo 1 bloqueo de hilo no logra nada. El objetivo del bloqueo es bloquear otros hilos, ¡pero no funciona si nadie más revisa el bloqueo!

Ahora, no necesita preocuparse por la corrupción de la memoria con un int de 32 bits, porque la escritura es atómica, pero eso no significa necesariamente que pueda estar libre de bloqueos.

En su ejemplo, es posible obtener semántica cuestionable:

example = 10 Thread A: Add(10) read example (10) Thread B: Read() read example (10) Thread A: write example (10 + 10)

lo que significa que ThreadB comenzó a leer el valor del ejemplo después de que el thread A comenzó su actualización, pero lea el valor pre-actualizado. Si eso es un problema o no depende de lo que se supone que debe hacer este código, supongo.

Como este es un código de ejemplo, puede ser difícil ver el problema allí. Pero imagina la función de contador canónico:

class Counter { static int nextValue = 0; static IEnumerable<int> GetValues(int count) { var r = Enumerable.Range(nextValue, count); nextValue += count; return r; } }

Entonces, la siguiente situación:

nextValue = 9; Thread A: GetValues(10) r = Enumerable.Range(9, 10) Thread B: GetValues(5) r = Enumerable.Range(9, 5) nextValue += 5 (now equals 14) Thread A: nextValue += 10 (now equals 24)

El nextValue se incrementa de forma adecuada, pero los rangos devueltos se superponen. Los valores de 19 - 24 nunca fueron devueltos. Puede solucionar esto bloqueando la asignación de varr y nextValue para evitar que se ejecute cualquier otro subproceso al mismo tiempo.


Todo depende del contexto. Al tratar con tipos integrales o referencias, es posible que desee utilizar miembros del sistema. Threading . Clase interbloqueada .

Un uso típico como:

if( x == null ) x = new X();

Se puede reemplazar con una llamada a Interlocked.CompareExchange () :

Interlocked.CompareExchange( ref x, new X(), null);

Interlocked.CompareExchange () garantiza que la comparación y el intercambio se realizan como una operación atómica.

Otros miembros de la clase Interlocked, como Add () , Decrement () , Exchange () , Increment () y Read () realizan todas sus operaciones atómicamente. Lea la documentación en MSDN.


en general, los bloqueos solo son necesarios cuando el valor será modificado

EDITAR: El excelente resumen de Mark Brackett es más apropiado:

"Se requieren bloqueos cuando quieres que una operación que no sea atómica sea atómica"

en este caso, leer un entero de 32 bits en una máquina de 32 bits es presumiblemente una operación atómica ... ¡pero tal vez no! Tal vez la palabra clave volátil puede ser necesaria.