Compartir a través de


Introducción a 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 diferentes para proteger el mismo recurso, eludirá la protección proporcionada por un primitivo de sincronización.

Clase WaitHandle y tipos de sincronización ligeros

Varios primitivos de sincronización de .NET derivan de la System.Threading.WaitHandle clase , que encapsula un identificador de sincronización de sistema operativo nativo y usa un mecanismo de señalización para la interacción del subproceso. Estas 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 a un grupo de recursos simultáneamente. El estado de un semáforo se establece en señalizado cuando su recuento es mayor que cero y no asignado 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 obtiene 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 deriva de EventWaitHandle y, cuando se indica, permanece en un estado activado hasta que se llama al método Reset.

En .NET Framework, dado que WaitHandle deriva de System.MarshalByRefObject, estos tipos se pueden usar para sincronizar las actividades de los subprocesos a través de los límites del dominio de aplicación.

En .NET Framework, .NET Core y .NET 5+, algunos de estos tipos pueden representar identificadores 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, consulte 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. Use 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 una variedad de primitivos de sincronización para controlar el acceso a un recurso compartido por varios subprocesos.

Monitor (clase)

La System.Threading.Monitor clase 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 el 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 los 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, consulte la referencia de API Monitor .

Nota:

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

Mutex (clase)

La System.Threading.Mutex clase , 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, Mutex la clase se puede usar 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 mutex con nombre, utilice un constructor de mutex que especifique 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, consulte el artículo Mutex y la referencia de la API Mutex.

Estructura SpinLock

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

Para obtener más información sobre las ventajas y desventajas de usar spinlock, consulte el artículo SpinLock y la referencia de API SpinLock.

ReaderWriterLockSlim (clase)

La System.Threading.ReaderWriterLockSlim clase concede acceso exclusivo a un recurso compartido para escribir y permite que varios subprocesos accedan al recurso simultáneamente para su lectura. Es posible que quiera usar ReaderWriterLockSlim para sincronizar el acceso a una estructura de datos compartida que admita operaciones de lectura seguras para subprocesos, pero requiere acceso exclusivo para realizar operaciones 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, consulte la referencia de API ReaderWriterLockSlim .

Clases Semaphore y SemaphoreSlim

Las System.Threading.Semaphore clases y System.Threading.SemaphoreSlim limitan el número de subprocesos que pueden tener acceso a un recurso compartido o a un grupo de recursos simultáneamente. Los subprocesos adicionales que solicitan el recurso esperan hasta que cualquier subproceso libere el semáforo. Dado que el semáforo no tiene afinidad de hilo, un hilo 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 único límite de proceso.

En Windows, puede usar Semaphore para la sincronización entre procesos. Para ello, cree una Semaphore instancia que represente un semáforo del sistema con nombre utilizando 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

La interacción de subprocesos (o señalización de subprocesos) significa que un subproceso debe esperar una notificación, o una señal, de uno o varios subprocesos para continuar. Por ejemplo, si el subproceso A llama al Thread.Join método del subproceso B, el subproceso A se bloquea hasta que se complete el subproceso B. Los primitivos de sincronización descritos 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 mediante la adquisición del bloqueo.

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

EventWaitHandle, AutoResetEvent, ManualResetEvent y ManualResetEventSlim (clases)

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

Un evento de sincronización puede estar en un estado 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 ello, cree una EventWaitHandle instancia que represente un evento de sincronización del sistema con nombre mediante uno de los constructores EventWaitHandle que especifica un nombre o el EventWaitHandle.OpenExisting método .

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

Clase CountdownEvent

La System.Threading.CountdownEvent clase representa un evento que se establece cuando su recuento es cero. Aunque CountdownEvent.CurrentCount es mayor que cero, un subproceso que llama a CountdownEvent.Wait se bloquea. Llame CountdownEvent.Signal para disminuir el recuento de un evento.

A diferencia de ManualResetEvent o ManualResetEventSlim, que se puede usar para desbloquear varios subprocesos con una señal de un subproceso, puede usar CountdownEvent para desbloquear uno o más subprocesos con señales de varios subprocesos.

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

Barrier (clase)

La System.Threading.Barrier clase representa una barrera de ejecución de subprocesos. Un subproceso que llama al Barrier.SignalAndWait método indica que alcanzó la barrera y espera hasta que otros subprocesos participantes lleguen a 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 varios subprocesos requieren los resultados de otros subprocesos antes de continuar con la siguiente fase de cálculo.

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

Interlocked (clase)

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

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

Estructura SpinWait

La estructura System.Threading.SpinWait proporciona compatibilidad para la espera basada en ciclos. Es posible que quiera usarlo cuando un subproceso tenga que esperar a que se indique un evento o se cumpla una condición, pero cuando se espera que el tiempo de espera real sea menor que el tiempo de espera necesario mediante un identificador de espera o bloqueando 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, consulte el artículo SpinWait y la referencia de API SpinWait .

Consulte también