2010-04-06 29 views
27

Así que he leído en torno a que en lugar de llamar a un evento directamente con¿Los controladores de eventos no están seguros?

if (SomeEvent != null) 
    SomeEvent(this, null); 

que debería estar haciendo

SomeEventHandler temp = SomeEvent; 
if (temp != null) 
    temp(this, null); 

¿Por qué es esto así? ¿Cómo se convierte la segunda versión en un hilo seguro? cual es la mejor practica?

+0

Al leer las respuestas tentativas y calificadas aquí, tengo la sensación de que el manejo de eventos en C# está estrechamente vinculado, propenso a errores y no se entiende muy bien. – micahhoover

Respuesta

13

Los eventos son realmente azúcar sintáctica sobre una lista de delegados. Cuando invocas el evento, esto realmente itera sobre esa lista e invoca a cada delegado con los parámetros que has pasado.

El problema con los subprocesos es que podrían estar agregando o eliminando elementos de esta colección suscribiéndose/desuscribiéndose. Si lo hacen mientras itera la colección, esto causará problemas (creo que se lanza una excepción)

La intención es copiar la lista antes de iterarla, para que esté protegido contra cambios a la lista.

Nota: Sin embargo, ahora es posible invocar a su oyente incluso después de anular la suscripción, por lo que debe asegurarse de manejar esto en su código de escucha.

+2

En realidad, no causará ningún problema mientras itera sobre la colección. Los delegados en juego aquí son inmutables. El único problema es la fracción de segundo antes de comprobar si alguien se ha suscrito y realmente llama a los controladores de eventos. Una vez que haya comenzado a ejecutarlos, ningún cambio de fondo afectará la invocación actual. –

5

La mejor práctica es la segunda forma. La razón es que otro subproceso podría anular o alterar SomeEvent entre la prueba 'if' y la invocación.

+0

¿Por qué no puede suceder eso en la segunda declaración? – Daniel

+2

La lectura es de 'SomeEvent' es atómica, es decir, sucede todo el tiempo o nada. Por lo tanto 'temp' no se puede modificar fuera de ese hilo debido a que es local. – user7116

+0

Ooops 's/Thus/Also /' – user7116

2

Here es una buena redacción sobre eventos .NET y condiciones de carrera con hilos. Cubre algunos escenarios comunes y tiene algunas buenas referencias en él.

Espero que esto ayude.

+0

Gracias por el enlace – Daniel

29

IMO, las otras respuestas pierden un detalle clave - que los delegados (y por lo tanto los eventos) son inmutables. La importancia de esto es que la suscripción o anulación de la suscripción a un controlador de eventos no se agrega a una lista, sino que reemplaza la lista por una nueva con un elemento adicional (o uno menos) en ella.

Desde referencias son atómicas, esto significa que en el punto que lo hace:

var handler = SomeEvent; 

Ahora tiene un rígida instancia que no puede cambiar, incluso si en la próxima picosegundos otros cancela la suscripción de hilo (que causa la campo de evento real para convertirse en null).

Así que prueba null e invocarlo, y todo está bien. Nota por supuesto que hay todavía el escenario confuso del evento siendo elevado en un objeto que piensa que se anuló hace un picosegundo!

+0

Esto resuelve una preocupación, pero ¿el reemplazo se realiza de forma segura para garantizar que se realicen todas las operaciones solicitadas? Es decir, si dos subprocesos intentan suscribir delegados distintos al mismo tiempo, ¿se garantiza que ambos se suscribirán al final o es posible que una de esas suscripciones falle silenciosamente? – binki

+1

@binki sí; utiliza un bucle de intercambio entrelazado (compilador moderno) o una región sincronizada ('lock') (compiladores más antiguos) –

Cuestiones relacionadas