So beantworten Sie Ihre Fragen:
- Das Auslösen eines Ereignisses blockiert den Thread, wenn die Ereignishandler alle synchron implementiert sind.
- Die Ereignishandler werden nacheinander in der Reihenfolge ausgeführt, in der sie das Ereignis abonniert haben.
Auch ich war neugierig auf den internen Mechanismus event
und die damit verbundenen Operationen. Also schrieb ich ein einfaches Programm und ildasm
stöberte in seiner Implementierung herum.
Die kurze Antwort lautet
- Es gibt keine asynchrone Operation zum Abonnieren oder Aufrufen der Ereignisse.
- Das Ereignis wird mit einem Hintergrunddelegatenfeld desselben Delegatentyps implementiert
- Das Abonnieren ist abgeschlossen mit
Delegate.Combine()
- Das Abbestellen erfolgt mit
Delegate.Remove()
- Das Aufrufen erfolgt durch einfaches Aufrufen des endgültigen kombinierten Delegaten
Folgendes habe ich getan. Das Programm, das ich verwendet habe:
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();
}
}
Hier ist die Implementierung von Foo:
Beachten Sie, dass es ein Feld OnCall
und ein Ereignis gibt OnCall
. Das Feld OnCall
ist offensichtlich die Backing-Eigenschaft. Und es ist nur ein Func<int, string>
, nichts Besonderes hier.
Nun sind die interessanten Teile:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- und wie
OnCall
wird in aufgerufenDo()
Wie wird das Abonnieren und Abbestellen implementiert?
Hier ist die abgekürzte add_OnCall
Implementierung in CIL. Der interessante Teil ist, dass Delegate.Combine
zwei Delegaten verkettet werden.
.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
Ebenso Delegate.Remove
wird in verwendet remove_OnCall
.
Wie wird ein Ereignis aufgerufen?
Zum Aufrufen OnCall
in Do()
, ruft er einfach die letzte verkettete Delegierten nach dem ARG - Laden:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
Wie genau abonniert ein Abonnent eine Veranstaltung?
Und schließlich Main
erfolgt das Abonnieren des OnCall
Ereignisses , nicht überraschend, durch Aufrufen der add_OnCall
Methode für die Foo
Instanz.
Dies ist eine allgemeine Antwort und spiegelt das Standardverhalten wider:
Allerdings kann jede Klasse, die Ereignisse bereitstellt, ihr Ereignis asynchron implementieren. IDesign bietet eine Klasse namens
EventsHelper
, die dies vereinfacht.[Hinweis] Für diesen Link müssen Sie eine E-Mail-Adresse angeben, um die EventsHelper-Klasse herunterladen zu können. (Ich bin in keiner Weise verbunden)
quelle
Die für die Veranstaltung abonnierten Delegierten werden synchron in der Reihenfolge aufgerufen, in der sie hinzugefügt wurden. Wenn einer der Delegierten eine Ausnahme auslöst, werden die folgenden nicht aufgerufen.
Da Ereignisse mit Multicast-Delegaten definiert werden, können Sie mithilfe von Ihren eigenen Auslösemechanismus schreiben
und asynchrones Aufrufen der Delegierten;
quelle
Ereignisse sind nur Anordnungen von Delegierten. Solange der Delegatenanruf synchron ist, sind auch Ereignisse synchron.
quelle
Im Allgemeinen sind Ereignisse synchron. Es gibt jedoch einige Ausnahmen, z. B.
System.Timers.Timer.Elapsed
das Auslösen eines Ereignisses in einemThreadPool
Thread, wennSyncronisingObject
es null ist.Dokumente: http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
quelle
Ereignisse in C # werden synchron ausgeführt (in beiden Fällen), solange Sie keinen zweiten Thread manuell starten.
quelle
Ereignisse sind synchron. Aus diesem Grund funktioniert der Ereignislebenszyklus so, wie er funktioniert. Inits passieren vor dem Laden, Ladungen passieren vor dem Rendern usw.
Wenn für ein Ereignis kein Handler angegeben ist, wird der Zyklus nur durchlaufen. Wenn mehr als ein Handler angegeben ist, werden sie der Reihe nach aufgerufen und einer kann erst fortgesetzt werden, wenn der andere vollständig fertig ist.
Selbst asynchrone Anrufe sind bis zu einem gewissen Grad synchron. Es ist unmöglich, das Ende aufzurufen, bevor der Anfang abgeschlossen ist.
quelle