Ich untersuche Optionen für die asynchrone Ereignisverteilung in einer Komponente, die viele Teilnehmer für ihre Ereignisse hat. Beim Durchlesen der Optionen bin ich auf dieses Beispiel gestoßen:
%Vor% Abgesehen von der älteren Syntax (das Beispiel stammt von .NET 1.1), sieht es für mich so aus, als wäre es ein ernsthafter Ressourcenverlust. Es gibt keine Komplettierungsmethode, keine Abfrage zum Vervollständigen oder irgendeine andere Art, wie EndInvoke
aufgerufen wird.
Nach meinem Verständnis muss jedes BeginInvoke
ein entsprechendes EndInvoke
haben . Andernfalls sind% o_de% Objektinstanzen aushängend und (möglicherweise) Ausnahmen, die während der asynchronen Ereignisse ausgelöst wurden.
Mir ist klar, dass es einfach ist, das zu ändern, indem ich einen Callback anbiete und AsyncResult
mache, aber wenn ich das nicht muss. . .
Die Behandlung der asynchronen Ausnahmen ist eine ganz andere Sache, und kombiniert mit der Notwendigkeit, mit dem UI-Thread zu synchronisieren (d. h. EndInvoke
usw.) könnte die ganze Idee, diese asynchronen Benachrichtigungen auszuführen, sehr gut sein.
Also, zwei Fragen:
InvokeRequired
eine entsprechende BeginInvoke
? Ein Aufruf von BeginInvoke()
sollte mit einem EndInvoke()
gepaart werden, aber wenn Sie dies nicht tun, führt dies nicht zu einem Ressourcenleck. Das IAsyncResult
, das von BeginInvoke()
zurückgegeben wird, wird als Garbage Collection bezeichnet.
Der größte Fehler in diesem Code besteht darin, dass Sie in hohem Maße Ausnahmen ausgesetzt sind, die die Anwendung beenden. Vielleicht möchten Sie den Delegataufruf in einen Ausnahmebehandler umbrechen und sich Gedanken darüber machen, wie Sie die aufgetretenen Ausnahmen propagieren möchten (melden Sie den ersten, erstellen Sie eine Aggregatausnahme usw.).
Durch das Aufrufen einer Löschroutine mit BeginInvoke()
wird ein Thread aus der Thread-Warteschlange entfernt, um das Ereignis zu starten. Dies bedeutet, dass das Ereignis immer den Haupt-UI-Thread auslöst. Dies kann dazu führen, dass einige Event-Handler-Szenarien schwieriger zu handhaben sind (z. B. Aktualisieren der UI). Behandler müssen erkennen, dass sie SynchronizationContext.Send()
oder .Post()
aufrufen müssen, um mit dem primären UI-Thread zu synchronisieren. Natürlich gelten auch alle anderen Multi-Thread-Programmierfehler.
Nachdem ich eine Weile darüber nachgedacht habe, kam ich zu dem Schluss, dass es wahrscheinlich eine schlechte Idee ist, asynchrone Ereignisse in Windows Forms-Steuerelementen durchzuführen. Windows Forms-Ereignisse sollten im Benutzeroberflächenthread ausgelöst werden. Anderenfalls eine unangemessene Belastung für die Clients zu verursachen und möglicherweise mit AsyncResult
-Objekten und asynchronen Ausnahmen zu verfahren.
Es ist sauberer, die Clients ihre eigene asynchrone Verarbeitung auslösen zu lassen (mit BackgroundWorker
oder einer anderen Technik) oder das Ereignis synchron zu behandeln.
Es gibt natürlich Ausnahmen. System.Timers.Timer
zum Beispiel löst das Ereignis Elapsed
für einen Thread-Pool-Thread aus. Aber dann kommt die erste Benachrichtigung in einem Pool-Thread. Es sieht so aus, als ob die allgemeine Regel lautet: Erhebe die Ereignisse in demselben Thread, der die erste Benachrichtigung erhalten hat. Zumindest ist das die Regel, die am besten für mich funktioniert. Auf diese Weise gibt es keine Frage zu undichten Objekten.