Para responder tu pregunta:
- La generación de un evento bloquea el hilo si todos los controladores de eventos se implementan sincrónicamente.
- Los controladores de eventos se ejecutan secuencialmente, uno tras otro, en el orden en que están suscritos al evento.
Yo también tenía curiosidad por el mecanismo interno de event
y sus operaciones relacionadas. Así que escribí un programa simple y solía ildasm
hurgar en su implementación.
La respuesta corta es
- no hay ninguna operación asincrónica involucrada en la suscripción o invocación de eventos.
- El evento se implementa con un campo de delegado de respaldo del mismo tipo de delegado.
- la suscripción se realiza con
Delegate.Combine()
- darse de baja se hace con
Delegate.Remove()
- La invocación se realiza simplemente invocando al delegado combinado final
Esto es lo que hice. El programa que utilicé:
public class Foo
{
// cool, it can return a value! which value it returns if there're multiple
// subscribers? answer (by trying): the last subscriber.
public event Func<int, string> OnCall;
private int val = 1;
public void Do()
{
if (OnCall != null)
{
var res = OnCall(val++);
Console.WriteLine($"publisher got back a {res}");
}
}
}
public class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foo.OnCall += i =>
{
Console.WriteLine($"sub2: I've got a {i}");
return "sub2";
};
foo.OnCall += i =>
{
Console.WriteLine($"sub1: I've got a {i}");
return "sub1";
};
foo.Do();
foo.Do();
}
}
Aquí está la implementación de Foo:
Tenga en cuenta que hay un campo OnCall
y un evento OnCall
. El campo OnCall
es obviamente la propiedad de respaldo. Y es simplemente un Func<int, string>
, nada lujoso aquí.
Ahora las partes interesantes son:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- y como
OnCall
se invoca enDo()
¿Cómo se implementa la suscripción y cancelación de suscripción?
Aquí está la add_OnCall
implementación abreviada en CIL. La parte interesante es que se utiliza Delegate.Combine
para concatenar dos delegados.
.method public hidebysig specialname instance void
add_OnCall(class [mscorlib]System.Func`2<int32,string> 'value') cil managed
{
// ...
.locals init (class [mscorlib]System.Func`2<int32,string> V_0,
class [mscorlib]System.Func`2<int32,string> V_1,
class [mscorlib]System.Func`2<int32,string> V_2)
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall
// ...
IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
// ...
} // end of method Foo::add_OnCall
Asimismo, Delegate.Remove
se utiliza en remove_OnCall
.
¿Cómo se invoca un evento?
Para invocar OnCall
en Do()
, simplemente llama al delegado concatenada final después de cargar el arg:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
¿Cómo se suscribe exactamente un suscriptor a un evento?
Y finalmente, Main
no es sorprendente que la suscripción al OnCall
evento se realice llamando al add_OnCall
método en la Foo
instancia.
Esta es una respuesta general y refleja el comportamiento predeterminado:
Dicho esto, cada clase que proporciona eventos puede optar por implementar su evento de forma asincrónica. IDesign proporciona una clase llamada
EventsHelper
que simplifica esto.[Nota] este enlace requiere que proporciones una dirección de correo electrónico para descargar la clase EventsHelper. (No estoy afiliado de ninguna manera)
fuente
Los delegados suscritos al evento se invocan sincrónicamente en el orden en que se agregaron. Si uno de los delegados lanza una excepción, no se llamará a los siguientes.
Dado que los eventos se definen con delegados de multidifusión, puede escribir su propio mecanismo de activación utilizando
e invocar a los delegados de forma asincrónica;
fuente
Los eventos son simplemente conjuntos de delegados. Siempre que la llamada de delegado sea síncrona, los eventos también lo son.
fuente
En general, los eventos son sincrónicos. Sin embargo, hay algunas excepciones, como la
System.Timers.Timer.Elapsed
generación de un evento en unThreadPool
hilo siSyncronisingObject
es nulo.Documentos: http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
fuente
Los eventos en C # se ejecutan sincrónicamente (en ambos casos), siempre y cuando no inicie un segundo subproceso manualmente.
fuente
Los eventos son sincrónicos. Es por eso que el ciclo de vida del evento funciona como lo hace. Los inicios ocurren antes de las cargas, las cargas ocurren antes de los renders, etc.
Si no se especifica un controlador para un evento, el ciclo simplemente se desarrolla. Si se especifica más de un controlador, se llamarán en orden y uno no puede continuar hasta que el otro haya terminado por completo.
Incluso las llamadas asincrónicas son sincrónicas hasta cierto punto. Sería imposible llamar al final antes de que se complete el comienzo.
fuente