Condividi tramite


Panoramica del modello asincrono basato su eventi

Le applicazioni che eseguono molte attività contemporaneamente, ma rimangono reattive all'interazione dell'utente, spesso richiedono una progettazione che usa più thread. Lo System.Threading spazio dei nomi fornisce tutti gli strumenti necessari per creare applicazioni multithreading ad alte prestazioni, ma l'uso di questi strumenti richiede un'esperienza significativa con la progettazione di software multithreading. Per applicazioni multithreading relativamente semplici, il BackgroundWorker componente offre una soluzione semplice. Per applicazioni asincrone più sofisticate, è consigliabile implementare una classe conforme al modello asincrono basato su eventi.

Il modello asincrono basato su eventi rende disponibili i vantaggi delle applicazioni multithreading nascondendo molti dei problemi complessi intrinseci nella progettazione multithreading. L'uso di una classe che supporta questo modello consente di:

  • Eseguire attività che richiedono molto tempo, ad esempio download e operazioni di database, "in background", senza interrompere l'applicazione.

  • Eseguire più operazioni contemporaneamente, ricevere notifiche al termine di ogni operazione.

  • Attendi che le risorse siano disponibili senza interrompere ("blocco") l'esecuzione dell'applicazione.

  • Comunicare con le operazioni asincrone in sospeso usando il modello familiare di eventi e delegati. Per altre informazioni sull'uso di gestori eventi e delegati, vedere Eventi.

Una classe che supporta il modello asincrono basato su eventi avrà uno o più metodi denominati MethodNameAsync. Questi metodi possono eseguire il mirroring delle versioni sincrone, che eseguono la stessa operazione sul thread corrente. La classe può anche avere un evento MethodNameCompleted e può avere un metodo MethodNameAsyncCancel (o semplicemente CancelAsync).

PictureBox è un componente tipico che supporta il modello asincrono basato su eventi. È possibile scaricare un'immagine in modo sincrono chiamando il metodo Load, ma se l'immagine è grande o se la connessione di rete è lenta, l'applicazione smetterà di rispondere fino al completamento dell'operazione di download e fino a quando la chiamata a Load non restituisce.

Se si vuole che l'applicazione continui a essere in esecuzione durante il caricamento dell'immagine, è possibile chiamare il LoadAsync metodo e gestire l'evento LoadCompleted , esattamente come si gestirà qualsiasi altro evento. Quando si chiama il LoadAsync metodo , l'applicazione continuerà a essere eseguita mentre il download procede su un thread separato ("in background"). Il gestore eventi verrà chiamato al termine dell'operazione di caricamento dell'immagine e il gestore eventi può esaminare il AsyncCompletedEventArgs parametro per determinare se il download è stato completato correttamente.

Il modello asincrono basato su eventi richiede che un'operazione asincrona possa essere annullata e il controllo PictureBox soddisfi questo requisito tramite il suo metodo CancelAsync. La chiamata CancelAsync invia una richiesta per arrestare il download in sospeso e, quando l'attività viene annullata, viene generato l'evento LoadCompleted .

Attenzione

È possibile che il download venga completato esattamente come viene effettuata la CancelAsync richiesta, pertanto Cancelled potrebbe non riflettere la richiesta di annullamento. Si tratta di una race condition ed è un problema comune nella programmazione multithreading. Per ulteriori informazioni sui problemi relativi alla programmazione multithreading, vedere Managed Threading Best Practices.

Caratteristiche del modello asincrono basato su eventi

Il modello asincrono basato su eventi può assumere diverse forme, a seconda della complessità delle operazioni supportate da una determinata classe. Le classi più semplici possono avere un singolo metodo MethodNameAsync e un evento MethodNameCompleted corrispondente. Le classi più complesse possono avere diversi metodi MethodNameAsync , ognuno con un evento MethodNameCompleted corrispondente, nonché versioni sincrone di questi metodi. Le classi possono facoltativamente supportare l'annullamento, la creazione di report sullo stato e i risultati incrementali per ogni metodo asincrono.

Un metodo asincrono può anche supportare più chiamate in sospeso (più chiamate simultanee), consentendo al codice di chiamarlo qualsiasi numero di volte prima di completare altre operazioni in sospeso. La corretta gestione di questa situazione potrebbe richiedere all'applicazione di tenere traccia del completamento di ogni operazione.

Esempi di modello asincrono basato su eventi

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

Di seguito è 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 fittizia AsyncExample ha due metodi, entrambi che supportano chiamate sincrone e asincrone. I sovraccarichi sincroni si comportano come qualsiasi altra chiamata di metodo ed eseguono l'operazione sul thread chiamante; se l'operazione richiede molto tempo, potrebbe verificarsi un ritardo percepibile prima che la chiamata venga completata. Gli overload asincroni avviano l'operazione su un altro thread e terminano immediatamente, consentendo al thread chiamante di continuare mentre l'operazione viene eseguita "in background".

Sovraccarichi del Metodo Asincrono

Esistono potenzialmente due overload per le operazioni asincrone: chiamata singola e chiamata multipla. È possibile distinguere queste due forme in base alle firme del metodo: il form a chiamata multipla ha un parametro aggiuntivo denominato userState. Questo modulo consente al codice di chiamare Method1Async(string param, object userState) più volte senza attendere il completamento di operazioni asincrone in sospeso. Se invece si tenta di chiamare Method1Async(string param) prima del completamento di una chiamata precedente, il metodo genera un oggetto InvalidOperationException.

Il userState parametro per gli overload con più chiamate consente di distinguere tra le operazioni asincrone. Si specifica un valore univoco ,ad esempio un GUID o codice hash, per ogni chiamata a Method1Async(string param, object userState)e quando ogni operazione viene completata, il gestore eventi può determinare quale istanza dell'operazione ha generato l'evento di completamento.

Monitoraggio delle operazioni in sospeso

Se si utilizzano i sovraccarichi di invocazione multipla, il codice dovrà tenere traccia degli oggetti userState (ID attività) per le attività in sospeso. Per ogni chiamata a Method1Async(string param, object userState), in genere si genererà un nuovo oggetto univoco userState e lo si aggiungerà a una raccolta. Quando l'attività corrispondente a questo userState oggetto genera l'evento di completamento, l'implementazione del metodo di completamento esaminerà AsyncCompletedEventArgs.UserState e la rimuoverà dalla raccolta. Usato in questo modo, il userState parametro assume il ruolo di un ID attività.

Annotazioni

È necessario prestare attenzione a fornire un valore univoco per userState nelle invocazioni di overload di invocazioni multiple. Gli ID attività non univoci causeranno alla classe asincrona di generare un'eccezione ArgumentException.

Annullamento di operazioni in sospeso

È importante poter annullare le operazioni asincrone in qualsiasi momento prima del completamento. Le classi che implementano il modello asincrono basato su eventi avranno un CancelAsync metodo (se è presente un solo metodo asincrono) o un metodo MethodNameAsyncCancel (se sono presenti più metodi asincroni).

I metodi che consentono più chiamate accettano un userState parametro, che può essere usato per tenere traccia della durata di ogni attività. CancelAsync accetta un userState parametro che consente di annullare determinate attività in sospeso.

I metodi che supportano solo una singola operazione in sospeso alla volta, ad esempio Method1Async(string param), non sono annullabili.

Ricezione di aggiornamenti sul progresso e risultati incrementali

Una classe conforme al modello asincrono basato su eventi può facoltativamente fornire un evento per tenere traccia dello stato di avanzamento e dei risultati incrementali. Questo in genere verrà denominato ProgressChanged o MethodNameProgressChanged e il gestore eventi corrispondente accetta un ProgressChangedEventArgs parametro.

Il gestore eventi per l'evento ProgressChanged può esaminare la ProgressChangedEventArgs.ProgressPercentage proprietà per determinare la percentuale di completamento di un'attività asincrona. Questa proprietà varia da 0 a 100 e può essere usata per aggiornare la Value proprietà di un oggetto ProgressBar. Se sono in sospeso più operazioni asincrone, è possibile utilizzare la ProgressChangedEventArgs.UserState proprietà per distinguere l'operazione che segnala lo stato di avanzamento.

Alcune classi possono segnalare risultati incrementali man mano che le operazioni asincrone procedono. Questi risultati verranno archiviati in una classe che deriva da ProgressChangedEventArgs e verranno visualizzati come proprietà nella classe derivata. È possibile accedere a questi risultati nel gestore eventi per l'evento ProgressChanged , esattamente come si accede alla ProgressPercentage proprietà . Se sono in sospeso più operazioni asincrone, è possibile utilizzare la UserState proprietà per distinguere l'operazione che segnala i risultati incrementali.

Vedere anche