2012-07-16 11 views
11

Usando mis EventArgs cusom tales como:EventHandler <TEventArgs> seguridad de hilo en C#?

public event EventHandler<MyEventArgs> SampleEvent; 

de msdn por ejemplo:

public class HasEvent 
{ 
// Declare an event of delegate type EventHandler of 
// MyEventArgs. 

    public event EventHandler<MyEventArgs> SampleEvent; 

    public void DemoEvent(string val) 
    { 
    // Copy to a temporary variable to be thread-safe. 
     EventHandler<MyEventArgs> temp = SampleEvent; 
     if (temp != null) 
      temp(this, new MyEventArgs(val)); 
    } 
} 

tengo pregunta:

1) mirar el código marcado:

enter image description here

no veo una razón por la que debe copiarse a otro parámetro (complejo de los hilos)

ya que tenemos la keyowrd event, nadie puede tocar su lista de invocación (sin código extraño a la clase me refiero)

2) Si no estoy equivocado, la función DemoEvent debe ser virtual, para que pueda ser anulado en las clases sub ... (estoy seguro de que he visto en alguna parte)

lo extraño es que ReSharper también suele añadir virtuales :

por lo que si tengo este código:

enter image description here

me sugiere:

enter image description here

y cuando pulsarlo:

enter image description here

lo que de nuevo mi preguntas:

1) ¿Cuál es el escenario que resolverá esta línea EventHandler<MyEventArgs> temp = SampleEvent;, con respecto al hilo de seguridad?

2) ¿No debería la función ser virtual? (estoy seguro de que he visto este patrón con virtual)

+0

Resharper es solo un punto de vista correcto e incorrecto. No es un absoluto – podiluska

+1

Tenga en cuenta que en realidad hay dos condiciones de carrera aquí. Este cambio de código corrige solo una de estas razas. Consulte el excelente artículo de Eric Lippert, [Events and Races] (http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx) para obtener una explicación completa. – Brian

+1

posible duplicado de [C# Eventos y seguridad de subprocesos] (http://stackoverflow.com/questions/786383/c-sharp-events-and-thread-safety) –

Respuesta

10

¿Cuál es el escenario que esta línea EventHandler temp = SampleEvent; resolverá, con respecto al hilo de seguridad?

Imagínese usted hace esto:

if (SampleEvent != null) 
    SampleEvent(this, new MyEventArgs()); 

Si otro hilo se separará el controlador de eventos después de que el caso pero antes de la invocación a continuación, se le intenta llamar a un delegado null (y obtendrá una excepción).

¿No debería la función ser virtual?(Estoy seguro de que he visto este patrón con Virtual)

Sí, si la clase no es sealed entonces usted debe marcar esa función virtual (no es obligatorio pero es un modelo bien aceptado).

EDITAR

 
Time Thread 1          Thread 2 
1             obj.SampleEvent += MyHandler; 
2  if (SampleEvent != null)      
3  {           obj.SampleEvent -= MyHandler; 
4   SampleEvent(this, new MyEventArgs()); 
5  } 

En este caso, en el momento 4 que vas a llamar a un delegado null y va a lanzar una NullReferenceException. Ahora mira este código:

 
Time Thread 1          Thread 2 
1             obj.SampleEvent += MyHandler; 
2  var sampleEvent = SampleEvent; 
3  if (sampleEvent != null)      
4  {           obj.SampleEvent -= MyHandler; 
5   sampleEvent(this, new MyEventArgs()); 
6  } 

ahora en el momento de llamar sampleEvent 5 y sostiene el contenido edad de SampleEvent, en este caso no va a arrojar alguna excepción (pero vas a llamar MyHandler incluso si ha sido eliminado por el segundo hilo).

+0

Supongo que eso significa que algunos suscriptores aún pueden obtener un evento de evento activación incluso después de haber sido separados. – apokryfos

+2

@apokryfos sí, es verdad. Una solución mejor debería implicar una implementación de evento personalizado, pero incluso en este caso puede obtener un comportamiento no deseado con subprocesos y bloqueos). No hay ninguna solución buena y general (AFAIK), probablemente si tiene que evitar estos problemas, debe escribir un código personalizado largo/lento tanto para la implementación del evento como para la invocación. –

+0

_Si otro subproceso desconectará el controlador de eventos después de la if_ ... ok .... entonces, ¿cómo lo hizo seguro de subprocesos si después del _if statememnt_ otro subproceso eliminó la lista de invocación? –

5
if (SampleEvent != null) 
    SampleEvent(this, new MyEventArgs(val)); 

Esta es una carrera de enhebrado clásica. Otro hilo podría cancelar la suscripción a un controlador de eventos mientras se ejecuta este código. Lo que hace que el enunciado if() concluya que hay un suscriptor pero la llamada al evento falla con una NullReferenceException. Copiar el objeto delegado en una variable local garantiza que el código del cliente que cambia la referencia del objeto delegado anulando la suscripción a un controlador de eventos no causará un bloqueo. Sigue siendo un problema, llamará al controlador de eventos después de que se anuló la suscripción, pero esa es una carrera inevitable y no necesariamente fatal como el NRE y puede ser tratada por el controlador de eventos, a diferencia del NRE.

Sí, un método como este generalmente se hace protegido virtual y se llama OnSampleEvent() por lo que una clase derivada puede alterar el comportamiento de aumento de eventos.

+0

NRE? Qué es ? –

+1

Excepción de referencia nula :) – MBen

+0

_Copiando en una variable local_: no lo consigo, el delegado es un tipo de referencia, por lo que si otra variable es igual a este tipo de referencia, apunta a la misma ubicación de memoria. entonces a que te refieres ? –

Cuestiones relacionadas