2011-06-14 11 views
21

Si tiene una instancia de clase con una variable de miembro delegado y varios subprocesos invocan ese delegado (supongamos que apunta a un método de larga ejecución), ¿hay algún problema de contención?¿Los delegados de C# son seguros para subprocesos?

¿Necesita bloquear al delegado o es seguro para cada hilo llamar al método al que apunta el delegado, ya que cada hilo obtiene su propia pila de llamadas?

Respuesta

7

No, no son seguros para subprocesos y sí, usted tendrá que administrar la concurrencia usted mismo.

+0

¿Qué podría cambiar en un delegado? (Su comentario es correcto sobre los eventos, pero no veo qué puede cambiar sobre un delegado). –

+1

@ agent-j: eso es inmaterial; la implementación subyacente puede cambiar, pero la documentación actual es la que se indica. –

6

Directamente desde la documentación de MulticastDelegate:

estáticos públicos (Shared en Visual Basic) de este tipo son seguros para subprocesos. No se garantiza que ningún miembro de instancia sea seguro para subprocesos.

La clase Delegate contiene la misma información, por lo que no lo tiene.

2

Modificar un evento no es seguro para subprocesos, pero invocar a un delegado sí lo es. Dado que un delegado es inmutable, es seguro para subprocesos. Vea las observaciones aquí MSDN Delegate class:

tomado de here: En CLR a través de C# Richter señala algunos puntos sutiles sobre la invocación evento en multihilo clases:

Una cadena delegado es inmutable; se crea una nueva cadena para reemplazar la primera. Una cadena de delegados con cero suscriptores es nula. Eso significa que (si su evento es público) puede pasar de nulo a no nulo y viceversa, en cualquier momento.

+2

"No se garantiza que ningún miembro de instancia sea seguro para subprocesos". - desde su enlace – heisenberg

23

En cuanto a la invocación del delegado, la respuesta es sí.

Invocar a un delegado es seguro para subprocesos porque los delegados son inmutables. Sin embargo, debe asegurarse de que exista un delegado primero. Esta comprobación puede requerir algunos mecanismos de sincronización según el nivel de seguridad deseado.

Por ejemplo, lo siguiente podría arrojar un NullReferenceException si SomeDelegate se establecieron en nulo por otro hilo entre la verificación nula y la invocación.

if (SomeDelegate != null) 
{  
    SomeDelegate(); 
} 

El siguiente es un poco más seguro. Aquí estamos explotando el hecho de que los delegados son inmutables. Incluso si otro hilo modifica SomeDelegate, el código se endurece para evitar ese molesto NullReferenceException.

Action local = SomeDelegate; 
if (local != null) 
{ 
    local(); 
} 

Sin embargo, esto podría resultar en el delegado no se ejecuta si SomeDelegate se le asignó un valor no nulo en otro hilo. Esto tiene que ver con un problema sutil de barrera de memoria. El siguiente es el método más seguro.

Action local = Interlocked.CompareExchange(ref SomeDelegate, null, null); 
if (local != null) 
{ 
    local(); 
} 

En cuanto a la ejecución del procedimiento referenciado por el delegado de la respuesta es no.

Deberá proporcionar sus propias garantías de seguridad de hilos a través del uso de mecanismos de sincronización.Esto se debe a que CLR no proporciona automáticamente garantías de seguridad de subprocesos para la ejecución de delegados. Puede ser que el método no requiera ninguna sincronización adicional para que sea seguro, especialmente si nunca tiene acceso al estado compartido. Sin embargo, si el método lee o escribe desde una variable compartida, entonces deberá considerar cómo protegerse contra el acceso simultáneo desde múltiples hilos.

+0

O simplemente podría declararlo como 'evento público SomeHandler MyEvent = {} 'y se garantiza que no será nulo. –

+1

@Ed: Eso funciona para eventos, pero no funcionaría para delegados sin procesar ya que podría hacer 'SomeDelegate = null'. –

+0

Sí, eso es cierto, estaba (incorrectamente) asumiendo eventos. –

Cuestiones relacionadas