Condividi tramite


Suggerimenti per l'implementazione del modello asincrono basato su eventi

Aggiornamento: novembre 2007

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

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

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 che esegue il marshalling delle chiamate ai gestori eventi del client nel thread o nel contesto appropriato.

Nota:

È possibile ovviare a queste regole se si desidera esplicitamente evitare l'utilizzo del criterio 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 Form purché agli sviluppatori siano chiare le restrizioni implicate. Le applicazioni console non sincronizzano l'esecuzione delle chiamate di Post. È quindi possibile che gli eventi ProgressChanged non vengano generati in ordine. 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

Procedura dettagliata: implementazione di un componente che supporta il modello asincrono basato su eventi

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

Riferimenti

AsyncOperation

AsyncOperationManager

AsyncCompletedEventArgs

ProgressChangedEventArgs

BackgroundWorker

Altre risorse

Programmazione multithreading con il modello asincrono basato su eventi