2009-03-18 14 views
8

Sto esaminando le opzioni per eseguire l'invio di eventi asincroni in un componente che ha molti sottoscrittori dei suoi eventi. Nella lettura delle opzioni, mi sono imbattuto in questo esempio:Aumentare gli eventi in modo asincrono

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); 
    } 
} 

Al di là della sintassi precedente (il campione era di .NET 1.1), sembra a me come questa è una grave perdita di risorse. Non esiste alcun metodo di completamento, nessun polling per il completamento o qualsiasi altro modo che verrà chiamato EndInvoke.

La mia comprensione è che ogni BeginInvokedeve avere un corrispondente EndInvoke. Altrimenti ci sono in sospeso le istanze degli oggetti AsyncResult, insieme alle (potenzialmente) eccezioni che sono state sollevate durante gli eventi asincroni.

Mi rendo conto che è abbastanza facile cambiarlo fornendo una richiamata e facendo un EndInvoke, ma se non è necessario. . .

La gestione delle eccezioni asincrone è completamente diversa e, combinata con la necessità di sincronizzarsi con il thread dell'interfaccia utente (ad esempio InvokeRequired, ecc.) Potrebbe benissimo racchiudere l'intera idea di queste notifiche asincrone.

Così, due domande:

  1. Am Ho ragione a credere che ogni BeginInvoke richiede un corrispondente EndInvoke?
  2. Oltre a ciò che ho notato sopra, ci sono altre insidie ​​a fare notifiche di eventi asincroni nelle applicazioni Windows Form?
+0

Perché si desidera effettuare la distribuzione in modo asincrono? Hai fatto un'analisi delle prestazioni che mostra questo sarebbe di beneficio? In caso contrario, più thread potrebbero peggiorare la situazione. –

+0

Sì, ho eseguito l'analisi delle prestazioni per determinare se l'elaborazione asincrona sarebbe un vantaggio. –

risposta

3

Una chiamata a BeginInvoke() dovrebbe essere accoppiato con un EndInvoke(), ma non lo fa, non si tradurrà in una perdita di risorse. Il valore IAsyncResult restituito da BeginInvoke() verrà raccolto automaticamente.

Il più grande trabocchetto in questo codice è che sei molto esposto alle eccezioni che terminano l'applicazione. Si potrebbe voler avvolgere l'invocazione del delegato in un gestore di eccezioni e riflettere su come si desidera propagare le eccezioni che si verificano (riportare il primo, produrre un'eccezione aggregata, ecc.).

L'invio di una cancellazione tramite BeginInvoke() interromperà la coda di thread per avviare l'esecuzione dell'evento. Ciò significa che l'evento scatterà sempre dal thread principale dell'interfaccia utente. Ciò potrebbe rendere più difficili da gestire alcuni scenari del gestore di eventi (ad esempio l'aggiornamento dell'interfaccia utente). Gli handler devono rendersi conto che devono chiamare SynchronizationContext.Send() o .Post() per sincronizzarsi con il thread principale dell'interfaccia utente. Ovviamente si applicano anche tutte le altre insidie ​​della programmazione multi-thread.

+0

Sì, il requisito che i clienti (vale a dire gli abbonati agli eventi) siano pronti a gestire le notifiche sui thread di pool potrebbe essere un killer qui. Sto pensando che i clienti dovrebbero iscriversi in modo asincrono se vogliono piuttosto che avere il controllo che lo costringe. –

0
  1. No. EndInvoke è necessaria solo se viene specificato un tipo di ritorno. Controlla questo: thread. Inoltre, ho postato questo thread che è semi correlato.

  2. Non posso davvero aiutarti con quello! :-) scusa.

+0

Sfortunatamente, il thread MSDN non è definitivo, ma mi dà alcune indicazioni su dove posso rintracciarlo. –

1

Dopo aver riflettuto su questo per un po ', sono giunto alla conclusione che è probabilmente una cattiva idea eseguire eventi asincroni nei controlli di Windows Form. Gli eventi di Windows Form dovrebbero essere generati sul thread dell'interfaccia utente.In caso contrario, ciò rappresenta un onere eccessivo per i client e potrebbe creare problemi con gli oggetti AsyncResult e le eccezioni asincrone.

È più semplice lasciare che i client eseguano il proprio processo asincrono (utilizzando BackgroundWorker o qualche altra tecnica) o gestiscano l'evento in modo sincrono.

Ci sono delle eccezioni, ovviamente. System.Timers.Timer, ad esempio, solleva l'evento Elapsed in un thread del pool di thread. Ma poi, la notifica iniziale arriva su un thread del pool. Sembra che la regola generale sia: aumentare gli eventi sullo stesso thread che ha ricevuto la notifica iniziale. Almeno, questa è la regola che funziona meglio per me. In questo modo non ci sono dubbi sulla perdita di oggetti.

Problemi correlati