2008-11-19 15 views

Respuesta

123

Gran pregunta. Tal vez estoy equivocado ... Déjame intentar ... Revisión # 2 de mi respuesta original ... con un poco más de comprensión. Gracias por hacer que me leído :)

bloqueo (obj)

  • es un CLR constructo que para (intra-objeto?) De sincronización de hilo. Asegura que solo un hilo puede tomar posesión del bloqueo del objeto & ingrese el bloque bloqueado de código. Otros hilos deben esperar hasta que el propietario actual renuncie al bloqueo saliendo del bloque de código. También se recomienda que bloquee un objeto miembro privado de su clase.

Monitores bloqueo (obj)

  • se implementa internamente usando un monitor. Debería preferir el bloqueo (obj) porque evita que se emborrache, como olvidar el procedimiento de limpieza. Es una prueba a prueba de idiotas si lo harás.
    El uso del monitor generalmente se prefiere a los mutex, ya que los monitores se diseñaron específicamente para .NET Framework y, por lo tanto, hacen un mejor uso de los recursos.

Uso de una cerradura o monitor es útil para prevenir la ejecución simultánea de secuencias de rosca sensible de código, pero estas construcciones no permiten un hilo para comunicar un evento a otro. Esto requiere los eventos de sincronización, que son objetos que tienen uno de dos estados, señalados y no señalizados, que se pueden usar para activar y suspender hilos. Mutex, semáforos son conceptos de nivel de sistema operativo. por ejemplo, con un mutex llamado se podía sincronizar varios (administrado) exes

objeto mutex (asegurando que sólo una instancia de la aplicación se ejecuta en la máquina.):

  • diferencia de los monitores, sin embargo, un mutex se puede usar para sincronizar subprocesos entre procesos. Cuando se utiliza para la sincronización entre procesos, un mutex se llama con el nombre mutex porque se va a utilizar en otra aplicación y, por lo tanto, no se puede compartir mediante una variable global o estática. Debe tener un nombre para que ambas aplicaciones puedan acceder al mismo objeto mutex. Por el contrario, la clase Mutex es un contenedor de una construcción Win32.Si bien es más potente que un monitor, un mutex requiere transiciones de interoperabilidad que son más costosas desde el punto de vista informático que las requeridas por la clase Monitor.

Semaphores (daño a mi cerebro).

  • Utilice la clase Semaphore para controlar el acceso a un grupo de recursos. Los subprocesos ingresan al semáforo llamando al método WaitOne, que se hereda de la clase WaitHandle, y liberan el semáforo llamando al método Release. El recuento en un semáforo se reduce cada vez que un subproceso ingresa al semáforo, y se incrementa cuando un subproceso libera el semáforo. Cuando el recuento es cero, las solicitudes subsiguientes se bloquean hasta que otros subprocesos liberen el semáforo. Cuando todos los hilos han liberado el semáforo, el recuento se encuentra en el valor máximo especificado cuando se creó el semáforo. Un subproceso puede ingresar al semáforo varias veces ... La clase de semáforo no impone la identidad del subproceso en WaitOne o Release .. la responsabilidad de los programadores no es estorbar. Los semáforos son de dos tipos: semáforos locales y semáforos del sistema . Si crea un objeto de semáforo utilizando un constructor que acepta un nombre, se asocia con un semáforo del sistema operativo de ese nombre. Los semáforos del sistema con nombre son visibles en todo el sistema operativo y se pueden usar para sincronizar las actividades de los procesos. Un semáforo local existe solo dentro de su proceso. Puede ser utilizado por cualquier hilo en su proceso que tenga una referencia al objeto semáforo local. Cada objeto de semáforo es un semáforo local separado.

THE PAGE TO READ - Thread Synchronization (C#)

+17

Usted afirma que 'Monitor' no permite la comunicación es incorrecta; aún puede 'Pulse', etc. con un' Monitor' –

+2

Vea una descripción alternativa de Semaphores - http://stackoverflow.com/a/40473/968003. Piensa en los semáforos como gorilas en un club nocturno. Hay una cantidad específica de personas permitidas en el club a la vez. Si el club está lleno, nadie puede ingresar, pero tan pronto como una persona se vaya, otra persona podría ingresar. –

13

Como se indica en ECMA, y como se puede observar a partir de métodos reflejaba la instrucción lock es básicamente equivalente a

object obj = x; 
System.Threading.Monitor.Enter(obj); 
try { 
    … 
} 
finally { 
    System.Threading.Monitor.Exit(obj); 
} 

A partir del ejemplo antes mencionado, vemos que los monitores pueden bloquear el objetos.

Los Mutexe son útiles cuando necesita sincronización entre procesos, ya que pueden bloquear en un identificador de cadena. El mismo identificador de cadena puede ser utilizado por diferentes procesos para adquirir el bloqueo.

Los semáforos son como los mutex en esteroides, permiten el acceso simultáneo al proporcionar un recuento máximo de acceso concurrente '. Una vez que se alcanza el límite, el semáforo comienza a bloquear cualquier acceso adicional al recurso hasta que una de las personas que llaman libere el semáforo.

+4

Este azúcar sintáctico ha cambiado ligeramente en C# 4 Revisa http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx –

27

Re "Utilización de otras clases de sincronización .Net" - algunos de los otros que debe saber sobre: ​​

También hay más construcciones de bloqueo (baja sobrecarga) en CCR/TPL (Parallel Extensions CTP) - pero IIRC, estas estarán disponibles en .NET 4.0

+0

Entonces si quiero una comunicación de señal simple (por ejemplo, completar una operación asincrónica) - debería Monitor.Pulse? o use SemaphoreSlim o TaskCompletionSource? – Vivek

+0

Utilice TaskCompletionSource para la operación asincrónica. Básicamente, deja de pensar en los hilos y comienza a pensar en las tareas (unidades de trabajo). Los hilos son un detalle de implementación y no son relevantes. Al devolver un TCS, puede devolver resultados, errores o cancelar el manejo y se puede componer fácilmente con otras operaciones asincrónicas (como async await o ContinueWith). –

9

Una advertencia adicional para bloquear cualquier Mutex compartido que haya identificado con una ID de cadena es que tendrá un mutex "Local \" predeterminado y no se compartirá entre sesiones en un entorno de servidor de terminal.

Prefijo su identificador de cadena con "Global \" para garantizar que el acceso a los recursos compartidos del sistema esté controlado adecuadamente. Solo me encontré con un montón de problemas sincronizando las comunicaciones con un servicio que se ejecutaba bajo la cuenta SYSTEM antes de darme cuenta de esto.

5

me gustaría tratar de evitar el "bloqueo()", "mutex" y "Monitor" si se puede ...

ver el nuevo espacio de nombres en .NET System.Collections.Concurrent 4
Tiene algunas buenas clases de colección compatibles con el proceso

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

rocas ConcurrentDictionary! ¡ya no hay bloqueo manual para mí!

+1

Evite el bloqueo pero use el Monitor? ¿Por qué? – mafu

+0

@mafutrct Porque necesita encargarse de la sincronización usted mismo. –

+0

Oh, ahora lo entiendo, querías evitar TODAS las tres ideas mencionadas. Parecía que usarías Monitor pero no usarías bloqueo/Mutex. – mafu

11

lo hice el apoyo de CLR clases & para roscar en DotGNU y tengo un par de pensamientos ...

menos que necesite cerraduras proceso cruzado siempre se debe evitar el uso de mutex & semáforos. Estas clases en .NET son envoltorios alrededor de Win32 Mutex y Semáforos y son bastante pesadas (requieren un cambio de contexto en Kernel que es caro, especialmente si su bloqueo no está en disputa).

Como se mencionan otras, la instrucción de bloqueo C# es compilador mágico para Monitor.Enter y Monitor.Exit (existente dentro de una try/finally).

Los monitores tienen un mecanismo de señal/espera simple pero potente que Mutexes no tiene a través de los métodos Monitor.Pulse/Monitor.Esperar. El equivalente de Win32 sería objetos de evento a través de CreateEvent que en realidad también existen en .NET como WaitHandles. El modelo Pulse/Wait es similar a pthread_signal y pthread_wait de Unix, pero son más rápidos porque pueden ser operaciones completamente en modo usuario en el caso no contencioso.

Monitor.Pulse/Wait es fácil de usar. En un hilo, bloqueamos un objeto, revisamos un indicador/estado/propiedad y si no es lo que estamos esperando, llame a Monitor. Espere que suelte el bloqueo y espere hasta que se envíe un pulso. Cuando la espera vuelve, volvemos y revisamos el indicador/estado/propiedad nuevamente. En el otro hilo, bloqueamos el objeto cada vez que cambiamos el indicador/estado/propiedad y luego llamamos PulseAll para activar cualquier hilo que esté escuchando.

A menudo queremos que nuestras clases sean seguras para hilos, por lo que ponemos bloqueos en nuestro código. Sin embargo, a menudo sucede que nuestra clase solo será utilizada por un hilo. Esto significa que los bloqueos ralentizan innecesariamente nuestro código ... aquí es donde las optimizaciones inteligentes en el CLR pueden ayudar a mejorar el rendimiento.

No estoy seguro acerca de la implementación de bloqueos de Microsoft, pero en DotGNU y Mono, un marcador de estado de bloqueo se almacena en el encabezado de cada objeto. Cada objeto en .NET (y Java) puede convertirse en un bloqueo, por lo que cada objeto debe admitir esto en su encabezado. En la implementación de DotGNU, hay un indicador que le permite usar una tabla hash global para cada objeto que se usa como un bloqueo; esto tiene el beneficio de eliminar una sobrecarga de 4 bytes para cada objeto. Esto no es bueno para la memoria (especialmente para sistemas integrados que no están muy roscados) pero tiene un impacto en el rendimiento.

tanto mono como DotGNU utilizar eficazmente los mutex para realizar el bloqueo/espera, pero usar un estilo spinlock compare-and-exchange operaciones para eliminar la necesidad de realizar realmente un duro cerraduras menos que sea realmente necesario:

Se puede ver un ejemplo de monitores ¿Cómo puede ser implementado aquí:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup

Cuestiones relacionadas