2009-03-18 13 views
8

Ich untersuche Optionen für die asynchrone Ereignisverteilung in einer Komponente, die viele Abonnenten für ihre Ereignisse hat. In den Optionen durchlesen, ich auf dieses Beispiel lief:Ereignisse asynchron auslösen

public event ValueChangedEvent ValueChanged; 
public void FireEventAsync(EventArgs e) 
{ 
    Delegate[] delegates = ValueChanged.GetInvocationList(); 
    foreach (Delegate d in delegates) 
    { 
     ValueChangedEvent ev = (ValueChangedEvent)d; 
     ev.BeginInvoke(e, null, null); 
    } 
} 

Jenseits der älteren Syntax (die Probe wurde von .NET 1.1), sieht es für mich so eine ernsthafte Ressource Leck ist. Es gibt keine Abschlussmethode, keine Abfrage zum Abschluss oder eine andere Art, wie EndInvoke aufgerufen wird.

Mein Verständnis ist, dass jeder BeginInvoke eine entsprechende EndInvoke haben muss. Sonst sind AsyncResult Objektinstanzen verfügbar, die herumschweben, zusammen mit (möglicherweise) Ausnahmen, die während der asynchronen Ereignisse ausgelöst wurden.

Ich realisiere, dass es einfach genug ist, das durch einen Rückruf zu ändern und eine EndInvoke tun, aber wenn ich nicht brauche. . . Die Handhabung der asynchronen Ausnahmen ist eine ganz andere Sache, und kombiniert mit der Notwendigkeit, mit dem UI-Thread (d. H. InvokeRequired, etc.) zu synchronisieren, könnte die ganze Idee, diese asynchronen Benachrichtigungen durchzuführen, sehr gut sein.

also zwei Fragen:

  1. Bin ich in der Annahme richtig, dass jeder BeginInvoke ein EndInvoke entsprechenden erfordert?
  2. Abgesehen von dem, was ich oben erwähnt habe, gibt es andere Probleme zu tun, asynchrone Ereignisbenachrichtigungen in Windows Forms-Anwendungen?
+0

Warum möchten Sie asynchron versenden? Haben Sie eine Leistungsanalyse durchgeführt, die zeigt, dass dies von Vorteil wäre? Wenn nicht, können mehrere Threads es schlechter machen. –

+0

Ja, ich habe die Leistungsanalyse durchgeführt, um festzustellen, ob die asynchrone Verarbeitung von Vorteil ist. –

Antwort

3

Ein Aufruf BeginInvoke() sollte mit einem EndInvoke() gepaart werden, aber nicht tut es nicht in einem Ressource-Leck führen. Die IAsyncResult, die von BeginInvoke() zurückgegeben wird, wird Müll gesammelt werden.

Der größte Fehler in diesem Code besteht darin, dass Sie in hohem Maße Ausnahmen ausgesetzt sind, die die Anwendung beenden. Sie können 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.).

Das Aufrufen einer Deletage mit BeginInvoke() entfernt einen Thread aus der Thread-Warteschlange, 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() anrufen müssen, um mit dem primären UI-Thread zu synchronisieren. Natürlich gelten auch alle anderen Multithread-Programmierfehler.

+0

Ja, die Anforderung, dass Clients (d. H. Ereignisabonnenten) bereit sein müssen, Benachrichtigungen auf Pool-Threads zu verarbeiten, könnte hier sehr gut sein. Ich denke, dass Clients asynchron abonnieren sollten, wenn sie wollen, anstatt die Kontrolle zu haben. –

0
  1. Nr EndInvoke wird nur dann, wenn ein Rückgabetyp erforderlich angegeben wird. Überprüfen Sie dies: thread. Außerdem postete ich diese thread, die halb verwandt ist.

  2. Ich kann Ihnen wirklich nicht helfen! :-) Es tut uns leid.

+0

Leider ist der MSDN-Thread nicht definitiv, aber es gibt mir einige Hinweise, wo ich das nachverfolgen kann. –

1

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 eine Unordnung mit AsyncResult Objekten und asynchronen Ausnahmen zu verursachen.

Es ist sauberer, wenn die Clients ihre eigene asynchrone Verarbeitung (unter Verwendung von BackgroundWorker oder einer anderen Technik) auslösen oder das Ereignis synchron behandeln.

Es gibt natürlich Ausnahmen. Beispielsweise löst System.Timers.Timer das Ereignis Elapsed in einem Threadpool-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 über undichte Objekte.

Verwandte Themen