Sincronización de datos para multiproceso

Cuando varios subprocesos pueden realizar llamadas a las propiedades y métodos de un solo objeto, es fundamental que esas llamadas se sincronicen. De lo contrario, un subproceso podría interrumpir lo que hace otro subproceso y el objeto podría dejarse en un estado no válido. La clase cuyos miembros están protegidos de tales interrupciones se conoce como segura para subprocesos.

.NET proporciona varias estrategias para sincronizar el acceso a los miembros estáticos y de instancia:

  • Regiones de código sincronizadas. Puede usar la clase Monitor o el soporte del compilador para esta clase para sincronizar únicamente el bloque de código que lo necesita, lo que mejora el rendimiento.

  • Sincronización manual. Puede usar los objetos de sincronización proporcionados por la biblioteca de clases .NET. Consulte Introducción a los primitivos de sincronización, que incluye una explicación de la Monitor clase .

  • Contextos sincronizados. Solo para las aplicaciones de .NET Framework, puede usar SynchronizationAttribute para habilitar la sincronización sencilla y automática de ContextBoundObject objetos.

  • Clases de colección en el espacio de nombres System.Collections.Concurrent. Estas clases proporcionan operaciones de adición y eliminación sincronizadas integradas. Para obtener más información, consulte Thread-Safe Colecciones.

Common Language Runtime proporciona un modelo de subproceso en el que las clases se dividen en una serie de categorías que se pueden sincronizar de varias maneras diferentes en función de los requisitos. En la tabla siguiente se muestra qué compatibilidad de sincronización se proporciona para campos y métodos con una categoría de sincronización determinada.

Category Campos globales Campos estáticos Métodos estáticos Campos de instancia Métodos de instancia Bloques de código específicos
Sin sincronización No No No No No No
Contexto sincronizado No No No Yes Yes No
Regiones de código sincronizadas No No Solo si está marcado No Solo si está marcado Solo si está marcado
Sincronización manual Manual Manual Manual Manual Manual Manual

Sin sincronización

Este es el valor predeterminado para los objetos . Cualquier subproceso puede acceder a cualquier método o campo en cualquier momento. Solo un subproceso a la vez debe tener acceso a estos objetos.

Sincronización manual

La biblioteca de clases .NET proporciona una serie de clases para sincronizar subprocesos. Consulte Introducción a los primitivos de sincronización.

Regiones de código sincronizadas

Puede usar la Monitor clase o una palabra clave del compilador para sincronizar bloques de código, métodos de instancia y métodos estáticos. No se admiten campos estáticos sincronizados.

Tanto Visual Basic como C# admiten el marcado de bloques de código con una palabra clave de lenguaje determinada, la lock instrucción en C# o la SyncLock instrucción en Visual Basic. Cuando se ejecuta el código en un subproceso, se realiza un intento de adquirir el bloqueo. Si otro subproceso ya lo ha adquirido, el subproceso se bloquea hasta que el bloqueo vuelva a estar disponible. Cuando el subproceso sale del bloque sincronizado de código, se libera el bloqueo, independientemente de cómo salga el subproceso del bloque.

Note

En .NET 9 y C# 13 o una versión posterior, si el destino lock es una instancia System.Threading.Lock dedicada, la instrucción lock usa Lock.EnterScope() para crear una región sincronizada.

Visual Basic no admite System.Threading.Lock en SyncLock, por lo tanto, siga usando SyncLock con un tipo de referencia privado dedicado.

Si el lock objetivo no es una instancia de Lock, entonces, para cualquier uso de SyncLock, el compilador usa Monitor.Enter y Monitor.Exit. Debido a esa implementación, todavía puede usar otros Monitor métodos en la región sincronizada.

También puede decorar un método con MethodImplAttribute con un valor de MethodImplOptions.Synchronized, lo que tiene el mismo efecto que usar Monitor o una de las palabras clave del compilador para bloquear todo el cuerpo del método.

Thread.Interrupt se puede usar para interrumpir un subproceso de operaciones de bloqueo, como esperar el acceso a una región sincronizada del código. Thread.Interrupt también se usa para interrumpir subprocesos de operaciones como Thread.Sleep.

Importante

No bloquee el tipo ( es decir, typeof(MyType) en C#, GetType(MyType) en Visual Basic o MyType::typeid en C++) para proteger static métodos (Shared métodos en Visual Basic). Use un objeto estático privado en su lugar. Del mismo modo, no use this en C# (Me en Visual Basic) para bloquear métodos de instancia. Use un objeto privado en su lugar. Una clase o instancia se puede bloquear mediante código distinto del suyo propio, lo que podría provocar interbloqueos o problemas de rendimiento.

Compatibilidad con el compilador

Tanto Visual Basic como C# admiten una palabra clave de lenguaje que usa Monitor.Enter y Monitor.Exit para bloquear el objeto. Visual Basic admite la instrucción SyncLock; C# admite la instrucción lock.

En ambos casos, si se produce una excepción en el bloque de código, el bloqueo adquirido mediante lock o SyncLock se libera automáticamente. Los compiladores de C# y Visual Basic emiten un bloque try/finally con Monitor.Enter al principio del intento y Monitor.Exit en el bloque finally. Si se produce una excepción dentro del bloque lock o SyncLock, el controlador finally se ejecuta para que pueda realizar cualquier tarea de limpieza.

Contexto sincronizado

Solo en aplicaciones de .NET Framework, puede usar SynchronizationAttribute en cualquier ContextBoundObject para sincronizar todos los métodos y campos de instancia. Todos los objetos del mismo dominio de contexto comparten el mismo bloqueo. Se permiten varios subprocesos para acceder a los métodos y campos, pero solo se permite un único subproceso en cualquier momento.

Consulte también