Como usted señala, en que múltiples hilos pueden acceder SomeEvent
al mismo tiempo, un hilo podría comprobar si SomeEvent
es nula y determinar que no es Justo después de hacerlo, otro hilo podría eliminar al último delegado registrado de SomeEvent
. Cuando el primer subproceso intenta aumentar SomeEvent
, se lanzará una excepción. Una forma razonable de evitar esta situación es:
protected virtual void OnSomeEvent(EventArgs args)
{
EventHandler ev = SomeEvent;
if (ev != null) ev(this, args);
}
Esto funciona porque cada vez que se agrega un delegado o se elimina de un evento usando las implementaciones predeterminadas de los descriptores de acceso y quitar, el Delegate.Combine y Delegate.Remove estática métodos son usados. Cada uno de estos métodos devuelve una nueva instancia de un delegado, en lugar de modificar la que se le pasó.
Además, la asignación de una referencia de objeto en .NET es atomic, y las implementaciones predeterminadas de los accesos de agregar y quitar eventos son synchronised. Entonces, el código anterior tiene éxito copiando primero el delegado de multidifusión del evento a una variable temporal. Cualquier cambio en SomeEvent después de este punto no afectará la copia que haya realizado y almacenado. Por lo tanto, ahora puede comprobar de forma segura si los delegados se registraron y posteriormente invocarlos.
Tenga en cuenta que esta solución resuelve un problema de carrera, concretamente el de un controlador de eventos que es nulo cuando se invoca. No maneja el problema donde un controlador de eventos está extinto cuando se invoca, o un controlador de eventos se suscribe después de que se toma la copia.
Por ejemplo, si un controlador de eventos depende del estado que se destruye tan pronto como el controlador no está suscrito, esta solución podría invocar un código que no se puede ejecutar correctamente. Ver Eric Lippert's excellent blog entry para más detalles. También, vea this StackOverflow question and answers.
EDITAR: Si está utilizando C# 6.0, entonces Krzysztof's answer parece un buen camino a seguir.
Tengo un problema con la declaración "Además, las asignaciones de referencias de objetos en .NET son seguras para hilos". ¿Seguro que te refieres a atómico? Por lo que yo sé, si el hilo A establece una referencia en la variable V, nada garantiza que el hilo B establecerá la referencia actualizada en la variable V, a menos que V sea volátil o se utilicen instrucciones de bloqueo al leer y escribir V. –
Eso también significa que tu ejemplo esta roto Si el subproceso A agregó controladores de eventos a SomeEvent, y luego el subproceso B invoca SomeEvent, bien podría suceder que el subproceso B vea SomeEvent como nulo, a menos que SomeEvent se haya declarado como volátil –
@Christophe, por "thread-safe", me refiero esa asignación de una referencia de objeto en un subproceso nunca se interrumpirá o se verá como inconsistente por otro subproceso. Como mencioné en mi actualización, definitivamente no es lo mismo que decir que el hilo A y el hilo B siempre tendrán la misma vista del evento. Todo lo que hace este ejemplo es evitar una condición de carrera específica, no todas las condiciones de carrera. – RoadWarrior