Suggerimenti per l'implementazione del modello asincrono basato su eventi
Il modello asincrono basato su eventi offre un sistema efficace per l'esposizione del comportamento asincrono nelle classi attraverso una semantica nota di eventi e delegati. Per implementare tale modello, è necessario soddisfare alcuni requisiti comportamentali specifici. Nelle sezioni riportate di seguito vengono illustrati i requisiti e le indicazioni da tenere presenti per l'implementazione di una classe che segue il modello asincrono basato su eventi.
Per informazioni generali, vedere Implementazione del modello asincrono basato su eventi.
Nell'elenco riportato di seguito sono illustrate le procedure consigliate prese in esame in questo argomento:
Garanzie comportamentali richieste
Completamento
Eventi Completed e classi EventArgs
Esecuzione simultanea di operazioni
Accesso ai risultati
Generazione di report sullo stato di avanzamento
Implementazione della proprietà IsBusy
Annullamento
Errori ed eccezioni
Thread e contesti
Indicazioni
Garanzie comportamentali richieste
Se si implementa il modello asincrono basato su eventi, è necessario fornire garanzie sul comportamento della classe e sull'affidabilità di tale comportamento per i client.
Completamento
Richiamare sempre il gestore eventi NomeMetodoCompleted in caso di completamento, errore o annullamento. È infatti consigliabile che le applicazioni non si trovino mai in una situazione permanente di inattività e di non completamento. Fa eccezione a questa regola il caso in cui l'operazione asincrona stessa sia progettata per non essere mai completata.
Eventi Completed e classi EventArgs
Per ogni metodo NomeMetodoAsync distinto, applicare i requisiti di progettazione seguenti:
Definire un evento NomeMetodoCompleted sulla stessa classe del metodo.
Definire una classe EventArgs e un delegato associato per l'evento NomeMetodoCompleted che deriva dalla classe AsyncCompletedEventArgs. Il nome predefinito della classe deve avere il formato NomeMetodoCompletedEventArgs.
Verificare che la classe EventArgs sia specifica dei valori restituiti del metodo NomeMetodo. Quando si utilizza la classe EventArgs, è consigliabile non richiedere mai agli sviluppatori di eseguire il cast del risultato.
Nell'esempio di codice riportato di seguito vengono illustrate rispettivamente un'implementazione valida e non valida del requisito di progettazione in questione.
[C#]
// Good design
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e)
{
DemoType result = e.Result;
}
// Bad design
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e)
{
DemoType result = (DemoType)(e.Result);
}
Non definire una classe EventArgs per restituire metodi che restituiscono void. Utilizzare invece un'istanza della classe AsyncCompletedEventArgs.
Assicurarsi di generare sempre l'evento MethodNameCompleted. Questo evento deve essere generato in caso di corretto completamento, errore o annullamento. È infatti consigliabile che le applicazioni non si trovino mai in una situazione permanente di inattività e di non completamento.
Assicurarsi di intercettare le eccezioni che si verificano nell'operazione asincrona e assegnare l'eccezione intercettata alla proprietà Error.
Se si è verificato un errore durante il completamento dell'attività, è possibile che i risultati non siano accessibili. Quando la proprietà Error non è null, assicurarsi che l'accesso a qualsiasi proprietà nella struttura EventArgs generi un'eccezione. Utilizzare il metodo RaiseExceptionIfNecessary per eseguire questa verifica.
Modellare un timeout come errore. Quando si verifica un timeout, generare l'evento MethodNameCompleted e assegnare un oggetto TimeoutException alla proprietà Error.
Se la classe supporta più richiami concorrenti, assicurarsi che l'evento MethodNameCompleted contenga l'oggetto userSuppliedState appropriato.
Assicurarsi che l'evento MethodNameCompleted venga generato nel thread appropriato e in un momento adeguato del ciclo di vita dell'applicazione. Per ulteriori informazioni, vedere la sezione Thread e contesti.
Esecuzione simultanea di operazioni
Se la classe supporta più richiami simultanei, consentire allo sviluppatore di tenere traccia di ogni richiamo separatamente definendo l'overload di NomeMetodoAsync che accetta un parametro di stato con valori di oggetto, o un ID attività, denominato userSuppliedState. Questo parametro deve essere sempre l'ultimo nella firma del metodo NomeMetodoAsync.
Se la classe definisce l'overload di NomeMetodoAsync che accetta un parametro di stato con valori di oggetto, o un ID attività, tenere traccia del ciclo di vita dell'operazione con tale ID e specificarlo nuovamente nel gestore completamento. Per l'esecuzione di tali operazioni sono disponibili classi di supporto specifiche. Per ulteriori informazioni sulla gestione della concorrenza, vedere Procedura dettagliata: implementazione di un componente che supporta il modello asincrono basato su eventi.
Se la classe definisce il metodo NomeMetodoAsync senza il parametro di stato e non supporta più richiami simultanei, assicurarsi che qualsiasi tentativo di richiamare NomeMetodoAsync prima del completamento del richiamo precedente dello stesso metodo generi una InvalidOperationException.
Normalmente non generare un'eccezione se il metodo NomeMetodoAsync senza il parametro userSuppliedState viene richiamato più volte determinando la presenza di più operazioni in attesa. È possibile generare un'eccezione quando la classe non può gestire questa situazione, ma si presuppone che gli sviluppatori possano gestire i diversi callback indistinguibili in corso.
Accesso ai risultati
Se durante l'esecuzione dell'operazione asincrona si è verificato un errore, è possibile che i risultati non siano accessibili. Verificare che l'accesso a qualsiasi proprietà in AsyncCompletedEventArgs, quando Error non è null, generi l'eccezione cui fa riferimento tale proprietà. La classe AsyncCompletedEventArgs fornisce il metodo RaiseExceptionIfNecessary a questo scopo.
Assicurarsi che i tentativi di accesso al risultato generino una InvalidOperationException che segnala l'avvenuto annullamento dell'operazione. Utilizzare il metodo AsyncCompletedEventArgs.RaiseExceptionIfNecessary per eseguire questa verifica.
Generazione di report sullo stato di avanzamento
Garantire il supporto della generazione di report sullo stato di avanzamento, se possibile, per consentire agli sviluppatori di offrire prestazioni ottimali dell'applicazione relativamente all'utilizzo di una classe.
Se si implementa un evento ProgressChanged/NomeMetodoProgressChanged, assicurarsi che non siano stati generati eventi di questo tipo per una determinata operazione asincrona dopo la generazione dell'evento NomeMetodoCompleted dell'operazione.
In caso di popolamento della classe ProgressChangedEventArgs standard, accertarsi che la proprietà ProgressPercentage possa essere sempre interpretata come percentuale. Non è necessario che il valore di percentuale sia preciso, purché si tratti di un valore di percentuale. Se l'unità di misura dei report sullo stato di avanzamento deve essere diversa da una percentuale, derivare una classe dalla classe ProgressChangedEventArgs e lasciare impostato su 0 il valore di ProgressPercentage. Evitare di utilizzare unità di misura dei report diverse dalla percentuale.
Verificare che l'evento ProgressChanged sia generato sul thread appropriato in un momento adeguato del ciclo di vita dell'applicazione. Per ulteriori informazioni, vedere la sezione Thread e contesti.
Implementazione della proprietà IsBusy
Non esporre una proprietà IsBusy se la classe supporta più richiami concorrenti. I proxy dei servizi Web XML, ad esempio, non espongono una proprietà IsBusy in quanto supportano più richiami concorrenti dei metodi asincroni.
La proprietà IsBusy deve restituire true dopo la chiamata del metodo NomeMetodoAsync e prima della generazione dell'evento NomeMetodoCompleted. In caso contrario, deve restituire false. I componenti BackgroundWorker e WebClient sono esempi di classi che espongono una proprietà IsBusy.
Annullamento
Garantire il supporto della funzione di annullamento, se possibile, per consentire agli sviluppatori di offrire prestazioni ottimali dell'applicazione relativamente all'utilizzo di una classe.
In caso di annullamento, impostare il flag Cancelled nell'oggetto AsyncCompletedEventArgs.
Assicurarsi che i tentativi di accesso al risultato generino una InvalidOperationException che segnala l'avvenuto annullamento dell'operazione. Utilizzare il metodo AsyncCompletedEventArgs.RaiseExceptionIfNecessary per eseguire questa verifica.
Verificare che le chiamate a un metodo di annullamento vengano eseguite correttamente senza mai generare un'eccezione. Generalmente, a un client viene notificato se un'operazione è realmente annullabile in qualsiasi momento mentre non viene notificato se un annullamento già avviato ha avuto esito positivo. All'applicazione viene invece sempre notificato l'avvenuto annullamento, poiché lo stato di completamento dipende anche dall'applicazione.
Generare l'evento NomeMetodoCompleted quando l'operazione viene annullata.
Errori ed eccezioni
- Intercettare le eccezioni che si verificano nell'operazione asincrona e impostare il valore della proprietà AsyncCompletedEventArgs.Error su tali eccezioni.
Thread e contesti
Per un corretto funzionamento della classe, è fondamentale che i gestori eventi del client siano richiamati sul thread o sul contesto appropriato al modello di applicazione specifico, incluse le applicazioni ASP.NET e Windows Form. Per garantire il comportamento corretto della classe asincrona con qualsiasi modello di applicazione sono disponibili due classi di supporto, AsyncOperation e AsyncOperationManager.
AsyncOperationManager fornisce un metodo, CreateOperation, che restituisce un oggetto AsyncOperation. Il metodo NomeMetodoAsync chiama CreateOperation e la classe utilizza l'oggetto AsyncOperation restituito per tenere traccia del ciclo di vita dell'attività asincrona.
Per generare report destinati al client sullo stato di avanzamento, sui risultati incrementali e sul completamento, chiamare i metodi Post e OperationCompleted sull'oggetto AsyncOperation. Viene restituito un oggetto AsyncOperation, che è responsabile del marshalling delle chiamate sui gestori eventi del client al thread o al contesto appropriato.
Nota |
---|
È possibile ovviare a queste regole se si desidera esplicitamente evitare l'utilizzo dei criteri del modello di applicazione, pur usufruendo degli altri vantaggi derivanti dal modello asincrono basato su eventi.È ad esempio possibile creare una classe threading Free operante in Windows Formpurché agli sviluppatori siano chiare le restrizioni implicate.Le applicazioni console non sincronizzano l'esecuzione delle chiamate di Post.L'ordine di generazione degli eventi ProgressChanged potrebbe pertanto non essere corretto.Se si desidera l'esecuzione serializzata delle chiamate Post, implementare e installare una classe System.Threading.SynchronizationContext. |
Per ulteriori informazioni sull'utilizzo di AsyncOperation eAsyncOperationManager per consentire operazioni asincrone, vedere Procedura dettagliata: implementazione di un componente che supporta il modello asincrono basato su eventi.
Indicazioni
È preferibile che ogni richiamo a un metodo sia indipendente dagli altri. Si consiglia di evitare di associare i richiami a risorse condivise. Se le risorse devono essere condivise tra i diversi richiami, è necessario fornire un meccanismo di sincronizzazione adeguato nell'implementazione.
Non sono consigliati progetti in cui al client sia richiesta l'implementazione della funzionalità di sincronizzazione. Si supponga il caso di un metodo asincrono che riceve un oggetto statico globale come parametro. Più richiami concorrenti di tale metodo potrebbero determinare un danneggiamento dei dati o deadlock.
Se si implementa un metodo con l'overload a più richiami (userState nella firma), la classe dovrà gestire un insieme di stati utente, o ID attività, e le relative operazioni in sospeso. Questo insieme deve essere protetto con aree lock, in quanto i diversi richiami aggiungono e rimuovono gli oggetti userState presenti al suo interno.
Riutilizzare le classi CompletedEventArgs quando possibile e appropriato. In tal caso, la denominazione non sarà coerente con il nome del metodo, poiché un determinato delegato e il tipo EventArgs non saranno collegati a un unico metodo Tuttavia, non è accettabile imporre agli sviluppatori di eseguire il cast del valore recuperato da una proprietà sull'oggetto EventArgs.
Se si sta creando una classe che deriva da Component, non implementare e non installare la propria classe SynchronizationContext. Sono i modelli di applicazione e non i componenti a controllare l'oggetto SynchronizationContext utilizzato.
L'utilizzo di qualsiasi tipo di multithreading determina la potenziale esposizione a bug seri e complessi. Pertanto, prima di implementare qualsiasi soluzione che preveda l'utilizzo del multithreading, vedere Suggerimenti per l'utilizzo del threading gestito.
Vedere anche
Attività
Procedura: utilizzare componenti che supportano il modello asincrono basato su eventi
Riferimenti
Concetti
Implementazione del modello asincrono basato su eventi
Quando implementare il modello asincrono basato su eventi
Suggerimenti per l'implementazione del modello asincrono basato su eventi
Altre risorse
Programmazione multithreading con il modello asincrono basato su eventi