Share via


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

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.

NotaNota

È 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

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

Riferimenti

AsyncOperation

AsyncOperationManager

AsyncCompletedEventArgs

ProgressChangedEventArgs

BackgroundWorker

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