Cenni preliminari sul modello asincrono basato su eventi

Aggiornamento: novembre 2007

Le applicazioni che eseguono più attività contemporaneamente, pur rimanendo disponibili all'utente, richiedono spesso una progettazione che preveda l'utilizzo più thread. Lo spazio dei nomi System.Threading offre tutti gli strumenti necessari per creare applicazioni multithreading a elevate prestazioni, per l'utilizzo dei quali è necessaria tuttavia una notevole esperienza nel campo della progettazione del software multithreading. Per applicazioni multithreading relativamente semplici, il componente BackgroundWorker fornisce una soluzione adeguata. Per applicazioni asincrone più complesse, si consiglia di implementare una classe che segua il modello asincrono basato su eventi.

Tale modello consente di usufruire dei vantaggi offerti dalle applicazioni multithreading nascondendo nel contempo gran parte degli aspetti complessi inerenti la progettazione multithreading. L'utilizzo di una classe che supporta questo modello consente di:

  • Eseguire in background attività che richiedono una quantità eccessiva di tempo, come download e operazioni di database, senza interrompere l'applicazione.

  • Eseguire più operazioni contemporaneamente, ricevendo notifiche relative al completamento di ognuna.

  • Attendere la disponibilità delle risorse senza interrompere o sospendere l'esecuzione dell'applicazione.

  • Comunicare con operazioni asincrone in sospeso mediante un modello noto di eventi e delegati. Per ulteriori informazioni sull'utilizzo di gestori eventi e delegati, vedere Eventi e delegati.

Una classe che supporta il modello asincrono basato su eventi disporrà di uno o più metodi denominati NomeMetodoAsync. Tali metodi possono eseguire il mirroring delle versioni sincrone che eseguono la stessa operazione sul thread corrente. La classe può anche disporre di un evento NomeMetodoCompleted e di un metodo NomeMetodoAsyncCancel (o semplicemente CancelAsync).

PictureBox è un componente tipico che supporta il modello asincrono basato su eventi. Per scaricare un'immagine in modo sincrono è possibile chiamare il relativo metodo Load, ma qualora le dimensioni dell'immagine fossero eccessive o la connessione di rete troppo lenta, l'esecuzione dell'applicazione verrà interrotta o sospesa fino al completamento dell'operazione di download e alla restituzione della chiamata a Load.

Per lasciare che l'applicazione continui a essere eseguita durante il caricamento dell'immagine, è possibile chiamare il metodo LoadAsync e gestire l'evento LoadCompleted analogamente a qualsiasi altro evento. Quando si chiama il metodo LoadAsync, l'esecuzione dell'applicazione procede mentre l'operazione di download continua su un altro thread, in background. Al termine dell'operazione di caricamento dell'immagine verrà chiamato il gestore eventi che potrà esaminare il parametro AsyncCompletedEventArgs per determinare se il download è stato eseguito.

Il modello asincrono basato su eventi richiede la possibilità di annullare un'operazione asincrona, requisito supportato dal controllo PictureBox per mezzo del relativo metodo CancelAsync. La chiamata del metodo CancelAsync determina l'invio di una richiesta di interruzione del download in sospeso. Quando l'attività viene annullata, viene generato l'evento LoadCompleted.

Attenzione:

È possibile che il download termini non appena viene effettuata la richiesta di CancelAsync. In tal caso, è possibile che la proprietà Cancelled non rifletta la richiesta di annullamento. Si tratta di una race condition, ovvero di un problema riscontrato comunemente nella programmazione multithreading. Per ulteriori informazioni sui problemi inerenti la programmazione multithreading, vedere Suggerimenti per l'utilizzo del threading gestito.

Caratteristiche del modello asincrono basato su eventi

Sono disponibili diversi tipi di modello asincrono basato su eventi, a seconda della complessità delle operazioni supportate da una determinata classe. Le classi più semplici possono presentare un solo metodo MethodNameAsync e un evento MethodNameCompleted corrispondente. Le classi più complesse possono invece disporre di diversi metodi MethodNameAsync, ognuno dei quali con un evento MethodNameCompleted associato, nonché di versioni sincrone di tali metodi. Per ogni metodo asincrono le classi possono supportare funzionalità di annullamento e generazione di report sullo stato di avanzamento, nonché risultati incrementali.

Un metodo asincrono può inoltre supportare più chiamate in sospeso, ovvero più richiami concorrenti, consentendo al codice di chiamarlo un numero indeterminato di volte prima del completamento di altre operazioni in sospeso. Per una gestione efficace di questa situazione può essere necessario che nell'applicazione venga tenuto traccia del completamento di ogni operazione.

Esempi del modello asincrono basato su eventi

I componenti SoundPlayer e PictureBox rappresentano implementazioni semplici del modello asincrono basato su eventi. I componenti WebClient e BackgroundWorker rappresentano implementazioni più complesse del modello in questione.

Di seguito viene riportata una dichiarazione di classe di esempio conforme al modello.

Public Class AsyncExample
    ' Synchronous methods.
    Public Function Method1(ByVal param As String) As Integer 
    Public Sub Method2(ByVal param As Double) 

    ' Asynchronous methods.
    Overloads Public Sub Method1Async(ByVal param As String) 
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object) 
    Public Event Method1Completed As Method1CompletedEventHandler

    Overloads Public Sub Method2Async(ByVal param As Double) 
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object) 
    Public Event Method2Completed As Method2CompletedEventHandler

    Public Sub CancelAsync(ByVal userState As Object) 

    Public ReadOnly Property IsBusy () As Boolean

    ' Class implementation not shown.
End Class
public class AsyncExample
{
    // Synchronous methods.
    public int Method1(string param);
    public void Method2(double param);

    // Asynchronous methods.
    public void Method1Async(string param);
    public void Method1Async(string param, object userState);
    public event Method1CompletedEventHandler Method1Completed;

    public void Method2Async(double param);
    public void Method2Async(double param, object userState);
    public event Method2CompletedEventHandler Method2Completed;

    public void CancelAsync(object userState);

    public bool IsBusy { get; }

    // Class implementation not shown.
}

La classe AsyncExample fittizia presenta due metodi che supportano entrambi i richiami sincroni e asincroni. Gli overload sincroni si comportano come qualsiasi chiamata di metodo ed eseguono le operazioni sul thread chiamante. Se l'operazione eseguita richiede tempo, è possibile che si verifichi un ritardo notevole nella restituzione della chiamata. Gli overload asincroni avviano l'operazione su un altro thread e vengono restituiti immediatamente consentendo al thread chiamante di continuare mentre l'operazione viene eseguita in background.

Overload di metodi asincroni

Esistono potenzialmente due overload per le operazioni asincrone: a richiamo singolo e a più richiami. È possibile distinguere i due tipi di overload dalle firme dei relativi metodi. Il tipo a più richiami presenta un parametro supplementare denominato userState e consente al codice di chiamare più volte Method1Async(string param, object userState) senza attendere il completamento di eventuali operazioni asincrone in sospeso. Se, invece, si tenta di chiamare Method1Async(string param) prima del completamento di un richiamo precedente, il metodo genera una InvalidOperationException.

Il parametro userState degli overload a più richiami consente di distinguere le diverse operazioni asincrone. Specificare un valore univoco, ad esempio un GUID o un codice hash, per ogni chiamata al metodo Method1Async(string param, object userState). Al termine di ogni operazione, il gestore eventi potrà individuare l'istanza dell'operazione che ha generato l'evento di completamento.

Registrazione delle operazioni in sospeso

Se si utilizzano gli overload a più richiami, il codice deve tenere traccia degli oggetti userState, o degli ID attività, relativi alle attività in sospeso. Per ogni chiamata al metodo Method1Async(string param, object userState), di solito viene generato un nuovo oggetto userState univoco che viene aggiunto a un insieme. Quando l'attività corrispondente a tale oggetto userState genera l'evento di completamento, l'implementazione del metodo di completamento esamina AsyncCompletedEventArgs.UserState e la rimuove dall'insieme. Se viene utilizzato in questo modo, il parametro userState funge da ID attività.

Nota:

È necessario specificare un valore univoco per il parametro userState nelle chiamate a overload a più richiami. La specifica di ID attività non univoci determina la generazione di una ArgumentException da parte della classe asincrona.

Annullamento delle operazioni in sospeso

È importante che sia possibile annullare le operazioni asincrone in qualsiasi momento prima che vengano completate. Le classi che implementano il modello asincrono basato su eventi dispongono di un metodo CancelAsync o NomeMetodoAsyncCancel se sono presenti rispettivamente più metodi asincroni o un solo metodo asincrono.

I metodi che consentono più richiami accettano un parametro userState che può essere utilizzato per tenere traccia del ciclo di vita di ogni attività. CancelAsync accetta un parametro userState che consente di annullare determinate attività in sospeso.

I metodi che supportano una sola operazione in sospeso alla volta, come Method1Async(string param), non sono annullabili.

Ricezione di aggiornamenti sullo stato di avanzamento e di risultati incrementali

Per tenere traccia dello stato di avanzamento e dei risultati incrementali, una classe conforme al modello asincrono basato su eventi può facoltativamente fornire un evento, che in genere è denominato ProgressChanged o NomeMetodoProgressChanged, e il cui gestore eventi accetta un parametro ProgressChangedEventArgs.

Il gestore eventi relativo all'evento ProgressChangedpuò esaminare la proprietàProgressChangedEventArgs.ProgressPercentage per determinare la percentuale di completamento di un'attività asincrona. Questa proprietà ha il valore compreso tra 0 e 100 e può essere utilizzata per aggiornare la proprietà Value di un oggetto ProgressBar. Se sono in sospeso più operazioni asincrone, è possibile utilizzare la proprietà ProgressChangedEventArgs.UserState per identificare l'operazione per la quale viene generato il report sullo stato di avanzamento.

Alcune classi possono generare un report sui risultati incrementali mano a mano che procede l'esecuzione delle operazioni asincrone. I risultati verranno memorizzati in una classe derivata da ProgressChangedEventArgs e verranno visualizzati come proprietà nella classe derivata. È possibile accedere ai risultati nel gestore eventi dell'evento ProgressChanged utilizzando la stessa procedura valida per la proprietà ProgressPercentage. Se sono in sospeso più operazioni asincrone, è possibile utilizzare la proprietà UserState per identificare l'operazione per la quale viene generato il report sui risultati incrementali.

Vedere anche

Attività

Procedura: utilizzare componenti che supportano il modello asincrono basato su eventi

Procedura: eseguire un'operazione in background

Procedura: implementare un form che utilizza un'operazione in background

Concetti

Suggerimenti per l'implementazione del modello asincrono basato su eventi

Quando implementare il modello asincrono basato su eventi

Riferimenti

ProgressChangedEventArgs

BackgroundWorker

AsyncCompletedEventArgs

Altre risorse

Programmazione multithreading con il modello asincrono basato su eventi