Información general sobre las primitivas de sincronización

.NET proporciona una variedad de tipos que puede usar para sincronizar el acceso a un recurso compartido o coordinar la interacción de subprocesos.

Importante

Use la misma instancia primitiva de sincronización para proteger el acceso de un recurso compartido. Si usa instancias primitivas de sincronización distintas para proteger el mismo recurso, se evitará la protección proporcionada por una primitiva de sincronización.

Clase WaitHandle y tipos de sincronización ligeros

Varias primitivas de sincronización de .NET derivan de la clase System.Threading.WaitHandle, que encapsula un controlador de sincronización del sistema operativo nativo y usa un mecanismo de señalización para la interacción de subprocesos. Esas clases incluyen:

  • System.Threading.Mutex, que concede acceso exclusivo a un recurso compartido. El estado de una exclusión mutua se señala si no es propiedad de ningún subproceso.
  • System.Threading.Semaphore, que limita el número de subprocesos que pueden tener acceso a un recurso compartido o grupo de recursos simultáneamente. El estado de un semáforo se establece como señalizado cuando su recuento es mayor que cero y como no señalizado cuando su recuento es cero.
  • System.Threading.EventWaitHandle, que representa un evento de sincronización de subprocesos y puede estar en un estado señalizado o no señalizado.
  • System.Threading.AutoResetEvent, que se deriva de EventWaitHandle y, cuando está señalizada, se restablece automáticamente a un estado no señalizado después de liberar un subproceso en espera único.
  • System.Threading.ManualResetEvent, que se deriva de EventWaitHandle y, cuando está señalizado, permanece en un estado señalizado hasta que se llama al método Reset.

En .NET Framework, como WaitHandle deriva de System.MarshalByRefObject, estos tipos se pueden usar para sincronizar las actividades de subprocesos entre los límites del dominio de la aplicación.

En .NET Framework, y .NET Core y .NET 5 y versiones posteriores, algunos de estos tipos pueden representar controladores de sincronización del sistema con nombre, que son visibles en todo el sistema operativo y se pueden usar para la sincronización entre procesos:

Para más información, vea la referencia de API WaitHandle.

Los tipos de sincronización ligeros no se basan en los controladores del sistema operativo subyacentes y suelen proporcionar un mejor rendimiento. Sin embargo, no se pueden usar para la sincronización entre procesos. Utilice esos tipos para la sincronización de subprocesos dentro de una aplicación.

Algunos de esos tipos son alternativas a los tipos derivados de WaitHandle. Por ejemplo, SemaphoreSlim es una alternativa ligera a Semaphore.

Sincronización del acceso a un recurso compartido

.NET proporciona un intervalo de primitivas de sincronización para controlar el acceso a un recurso compartido por varios subprocesos.

Monitor (clase)

La clase System.Threading.Monitor concede acceso mutuamente exclusivo a un recurso compartido mediante la adquisición o liberación de un bloqueo en el objeto que identifica el recurso. Mientras se mantiene un bloqueo, el subproceso que lo mantiene puede volver a adquirir y liberar dicho bloqueo. Ningún otro subproceso puede adquirir el bloqueo y el método Monitor.Enter espera hasta que el bloqueo se libera. El método Enter adquiere un bloqueo liberado. También puede usar el método Monitor.TryEnter para especificar la cantidad de tiempo durante el cual un subproceso intenta adquirir un bloqueo. Dado que la clase Monitor tiene afinidad de subproceso, el subproceso que adquirió un bloqueo debe liberarlo mediante una llamada al método Monitor.Exit.

Puede coordinar la interacción de subprocesos que adquieren un bloqueo en el mismo objeto mediante los métodos Monitor.Wait, Monitor.Pulse y Monitor.PulseAll.

Para más información, vea la referencia de API Monitor.

Nota

Use la instrucción lock en C# y la instrucción SyncLock en Visual Basic para sincronizar el acceso a un recurso compartido en lugar de usar la clase Monitor directamente. Esas instrucciones se implementan mediante los métodos Enter y Exit, y usa un bloqueo try…finally para asegurarse de que se libere el bloqueo.

Mutex (clase)

La clase System.Threading.Mutex, como Monitor, concede acceso exclusivo a un recurso compartido. Utilice una de las sobrecargas del método Mutex.WaitOne para solicitar la propiedad de una exclusión mutua. Al igual que Monitor, Mutex tiene afinidad de subproceso y el subproceso que adquirió una exclusión mutua debe liberarlo llamando al método Mutex.ReleaseMutex.

A diferencia de Monitor, la clase Mutex puede usarse para la sincronización entre procesos. Para ello, use una exclusión mutua con nombre, que es visible en todo el sistema operativo. Para crear una instancia de la exclusión mutua con nombre, use un constructor de exclusión mutua que especifica un nombre. También se puede llamar al método Mutex.OpenExisting para abrir una exclusión mutua del sistema existente.

Para más información, vea el artículo Mutexes y la referencia de API Mutex.

SpinLock (estructura)

La estructura System.Threading.SpinLock, como Monitor, concede acceso exclusivo a un recurso compartido en función de la disponibilidad de un bloqueo. Cuando SpinLock intenta adquirir un bloqueo que no está disponible, espera en un bucle, y realiza comprobaciones repetidamente hasta que dicho bloqueo esté disponible.

Para más información sobre las ventajas e inconvenientes del uso de un bloqueo de giro, vea el artículo SpinLock y la referencia de API SpinLock.

ReaderWriterLockSlim (clase)

La clase System.Threading.ReaderWriterLockSlim concede acceso exclusivo a un recurso compartido para escritura y permite que varios subprocesos accedan al recurso simultáneamente para lectura. Es posible que desee utilizar ReaderWriterLockSlim para sincronizar el acceso a una estructura de datos compartida que admita operaciones de lectura seguras para subprocesos, pero que requiera acceso exclusivo para realizar la operación de escritura. Cuando un subproceso solicita acceso exclusivo (por ejemplo, llamando al método ReaderWriterLockSlim.EnterWriteLock), las solicitudes posteriores del lector y el escritor se bloquean hasta que todos los lectores existentes han salido del bloqueo y el escritor ha entrado y salido de dicho bloqueo.

Para más información, vea la referencia de API ReaderWriterLockSlim.

Semaphore y SemaphoreSlim (clases)

Las clases System.Threading.Semaphore y System.Threading.SemaphoreSlim limitan el número de subprocesos que pueden tener acceso a un recurso compartido o grupo de recursos simultáneamente. Los demás subprocesos que soliciten el recurso esperarán hasta que un subproceso libere el semáforo. Dado que el semáforo no tiene afinidad de subproceso, un subproceso puede adquirir el semáforo y otro puede liberarlo.

SemaphoreSlim es una alternativa ligera a Semaphore y solo se puede usar para la sincronización dentro de un límite de un único proceso.

En Windows, puede usar Semaphore para la sincronización entre procesos. Para hacerlo, cree una instancia de Semaphore que represente un semáforo de sistema con nombre mediante el uso de uno de los constructores Semaphore que especifica un nombre o el método Semaphore.OpenExisting. SemaphoreSlim no es compatible con los semáforos con nombre del sistema.

Para más información, consulte el artículo Semaphore y SemaphoreSlim y la referencia de API Semaphore o SemaphoreSlim.

Interacción de subprocesos o señalización

Interacción de subprocesos (o señalización de subprocesos) significa que un subproceso debe esperar la notificación o una señal de uno o varios subprocesos para poder continuar. Por ejemplo, si un subproceso A llama al método Thread.Join del subproceso B, un subproceso A se bloquea hasta que el subproceso B finaliza. Las primitivas de sincronización descritas en la sección anterior proporcionan un mecanismo diferente para la señalización: al liberar un bloqueo, un subproceso notifica a otro subproceso que puede continuar adquiriendo el bloqueo.

En esta sección se describen construcciones adicionales de señalización proporcionados por. NET.

EventWaitHandle, AutoResetEvent, ManualResetEvent y ManualResetEventSlim (clases)

La clase System.Threading.EventWaitHandle representa un evento de sincronización de subprocesos.

Un evento de sincronización puede estar en un estado de no señalizado o señalizado. Cuando el estado de un evento es señalizado, un subproceso que llama a la sobrecarga WaitOne del evento se bloquea hasta que un evento se señaliza. El método EventWaitHandle.Set establece el estado de un evento en señalizado.

El comportamiento de una clase EventWaitHandle que se haya señalizado depende de su modo de restablecimiento:

En Windows, puede usar EventWaitHandle para la sincronización entre procesos. Para hacerlo, cree una instancia de EventWaitHandle que represente un evento de sincronización del sistema con nombre mediante uno de los constructores EventWaitHandle que especifique un nombre o el método EventWaitHandle.OpenExisting.

Para obtener más información, consulte el artículo EventWaitHandle. Para la referencia de API, consulte EventWaitHandle, AutoResetEvent, ManualResetEvent y ManualResetEventSlim.

Clase CountdownEvent

La clase System.Threading.CountdownEvent representa un evento que se establece cuando su recuento es cero. Mientras CountdownEvent.CurrentCount sea mayor que cero, un subproceso que llama a CountdownEvent.Wait está bloqueado. Llame a CountdownEvent.Signal para reducir el recuento de un evento.

En contraposición a ManualResetEvent o ManualResetEventSlim, que puede usar para desbloquear varios subprocesos con una señal de un subproceso, puede usar CountdownEvent para desbloquear uno o varios subprocesos con las señales de varios subprocesos.

Para más información, vea el artículo CountdownEvent y la referencia de API CountdownEvent.

Barrier (clase)

La clase System.Threading.Barrier representa una barrera de ejecución de subprocesos. Un subproceso que llama al método Barrier.SignalAndWait indica que ha alcanzado la barrera y espera hasta que otros subprocesos participantes alcancen la barrera. Cuando todos los subprocesos participantes alcancen la barrera, continúan y la barrera se restablece y se puede volver a usar.

Puede usar Barrier cuando uno o más subprocesos requieren los resultados de otros subprocesos antes de continuar con la siguiente fase del cálculo.

Para más información, vea el artículo Barrier y la referencia de API Barrier.

Interlocked (clase)

La clase System.Threading.Interlocked proporciona métodos estáticos que realizan operaciones atómicas simples en una variable. Esas operaciones atómicas incluyen la adición, el incremento y el decremento, el intercambio y el intercambio condicional que depende de una comparación, y la operación de lectura de un valor entero de 64 bits.

Para más información, vea la referencia de API Interlocked.

SpinWait (estructura)

La estructura System.Threading.SpinWait proporciona compatibilidad para la espera basada en ciclos. Puede que le interese utilizarla cuando un subproceso tiene que esperar a que se señalice un evento o se cumpla una condición, pero cuando el tiempo de espera real deba ser menor que el tiempo de espera necesario usando un identificador de espera o bloqueando de otro modo el subproceso. Si usa SpinWait, puede especificar un breve período de tiempo para girar durante la espera y después ceder (por ejemplo, esperando o entrando en modo de suspensión) solo si la condición no se cumplió en el tiempo especificado.

Para más información, vea el artículo SpinWait y la referencia de API SpinWait.

Vea también