Share via


Implementazione del modello asincrono basato su eventi

Aggiornamento: novembre 2007

Se la scrittura di una classe implica operazioni che possono causare ritardi notevoli, è possibile assegnare alla classe la funzionalità asincrona implementando Cenni preliminari sul modello asincrono basato su eventi.

Il modello asincrono basato su eventi costituisce un sistema standard per l'assemblaggio di una classe con funzionalità asincrone. Se implementata con le classi di supporto, come AsyncOperationManager, la classe in questione funziona con qualsiasi modello di applicazione, incluse le applicazioni ASP.NET, console e Windows Form.

Per un esempio relativo all'implementazione del modello asincrono basato su eventi, vedere Procedura: implementare un componente che supporta il modello asincrono basato su eventi.

Per operazioni asincrone semplici può risultare utile il componente BackgroundWorker. Per ulteriori informazioni sugli oggetti BackgroundWorker, vedere Procedura: eseguire un'operazione in background.

Nell'elenco riportato di seguito vengono illustrate le funzionalità del modello asincrono basato su eventi preso in esame in questo argomento.

  • Possibilità di implementazione del modello asincrono basato su eventi

  • Denominazione di metodi asincroni

  • Supporto facoltativo dell'annullamento

  • Supporto facoltativo della proprietà IsBusy

  • Supporto facoltativo della generazione di report sullo stato di avanzamento

  • Supporto facoltativo della restituzione di risultati incrementali

  • Gestione di parametri Out e Ref nei metodi

Possibilità di implementazione del modello asincrono basato su eventi

Implementare il modello asincrono basato su eventi nelle situazioni illustrate di seguito.

  • I client della classe non richiedono la disponibilità di oggetti WaitHandle eIAsyncResult per le operazioni asincrone. In altre parole, il polling e il metodo WaitAll o WaitAny devono essere generati dal client.

  • È richiesta la gestione delle operazioni asincrone da parte del client con il modello di evento/delegato noto.

Tutte le operazioni possono essere sottoposte a implementazione asincrona, ma si consiglia di prendere in considerazione quelle per le quali sono previste lunghe latenze. Sono particolarmente adatte le operazioni che prevedono la chiamata di un metodo da parte dei client e la notifica a questi ultimi del completamento, senza che siano necessari ulteriori interventi, nonché le operazioni con esecuzione continua, che implicano la notifica periodica ai client dell'avanzamento, dei risultati incrementali o delle modifiche dello stato.

Per ulteriori informazioni sui casi in cui è opportuno utilizzare il modello asincrono basato su eventi, vedere Quando implementare il modello asincrono basato su eventi.

Denominazione di metodi asincroni

Per ogni metodo NomeMetodo sincrono per il quale si desidera specificare una controparte asincrona:

Definire un metodo NomeMetodoAsync che esegua le operazioni riportate di seguito.

  • Restituisca il valore void.

  • Accetti gli stessi parametri del metodo NomeMetodo.

  • Accetti più richiami.

Facoltativamente definire un overload di NomeMetodoAsync identico a NomeMetodoAsync, ma con un parametro aggiuntivo con valori di oggetto denominato userState. Eseguire questa operazione se si è in grado di gestire più richiami concorrenti del metodo, caso in cui il valore di userState verrà restituito a tutti i gestori eventi per distinguere i diversi richiami, oppure scegliere di eseguirla in alternativa alla memorizzazione dello stato dell'utente per il successivo recupero.

Per ogni firma del metodo NomeMetodoAsync:

  1. Definire l'evento seguente nella stessa classe del metodo:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. Definire il delegato seguente e AsyncCompletedEventArgs. La definizione di questi elementi verrà eseguita esternamente alla classe, ma nello stesso spazio dei nomi.

    Public Delegate Sub MethodNameCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As MethodNameCompletedEventArgs)
    
    Public Class MethodNameCompletedEventArgs
        Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As MyReturnType
    End Property
    
    public delegate void MethodNameCompletedEventHandler(object sender, 
        MethodNameCompletedEventArgs e);
    
    public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        public MyReturnType Result { get; }
    }
    
    • Verificare che la classe di NomeMetodoCompletedEventArgs esponga i relativi membri come proprietà di sola lettura e non come campi, dal momento che questi ultimi impediscono l'associazione dati.

    • Non definire classi derivate da AsyncCompletedEventArgs per i metodi che non producono risultati. Utilizzare semplicemente un'istanza dell'oggetto AsyncCompletedEventArgs.

      Nota:

      È consentito, quando possibile e appropriato, riutilizzare il delegato e i tipi AsyncCompletedEventArgs. In tal caso, la denominazione non sarà coerente con il nome del metodo, poiché un determinato delegato e l'oggetto AsyncCompletedEventArgs non saranno collegati a un unico metodo

Supporto facoltativo dell'annullamento

Se la classe supporta l'annullamento delle operazioni asincrone, l'annullamento deve essere esposto al client come illustrato di seguito. Prima di definire il supporto dell'annullamento è necessario chiarire due punti decisionali:

  • La classe, incluse eventuali future aggiunte previste, presenta una sola operazione asincrona che supporta l'annullamento?

  • Per le operazioni asincrone che supportano l'annullamento sono consentite più operazioni in sospeso? In altre parole, il metodo NomeMetodoAsync accetta un parametro userState e consente più richiami prima di passare all'attesa del completamento di una delle operazioni in sospeso?

Utilizzare le risposte relative a queste due domande nella tabella riportata di seguito per determinare il tipo di firma del metodo di annullamento.

Visual Basic

 

Supporto di più operazioni simultanee

Una sola operazione alla volta

Una sola operazione asincrona nell'intera classe

Sub MethodNameAsyncCancel(ByVal userState As Object)
Sub MethodNameAsyncCancel()

Più operazioni asincrone nella classe

Sub CancelAsync(ByVal userState As Object)
Sub CancelAsync()

C#

 

Supporto di più operazioni simultanee

Una sola operazione alla volta

Una sola operazione asincrona nell'intera classe

void MethodNameAsyncCancel(object userState);
void MethodNameAsyncCancel();

Più operazioni asincrone nella classe

void CancelAsync(object userState);
void CancelAsync();

Se si definisce il metodo CancelAsync(object userState) , i client devono prestare attenzione nella scelta dei relativi valori di stato che consentono di distinguere i diversi metodi asincroni richiamati sull'oggetto e non solo i richiami di un singolo metodo asincrono.

La decisione di denominare la versione della singola operazione asincrona NomeMetodoAsyncCancel si basa sulla possibilità di individuare più facilmente il metodo in un ambiente di progettazione come IntelliSense di Visual Studio. Con questo sistema i membri correlati vengono raggruppati e distinti dagli altri che non presentano la funzionalità asincrona. Se si prevede di aggiungere ulteriori operazioni asincrone nelle versioni successive, è preferibile definire CancelAsync.

Non è opportuno definire più metodi della tabella precedente nella stessa classe, operazione che potrebbe generare confusione per la presenza di un numero eccessivo di metodi nell'interfaccia della classe.

Generalmente questi metodi vengono terminati immediatamente e l'operazione può essere effettivamente annullata o meno. Nel gestore eventi relativo all'evento NomeMetodoCompleted l'oggetto NomeMetodoCompletedEventArgs contiene un campo Cancelled che può essere utilizzato dai client per determinare se si è verificato l'annullamento.

Rispettare la semantica di annullamento illustrata in Suggerimenti per l'implementazione del modello asincrono basato su eventi.

Supporto facoltativo della proprietà IsBusy

Se la classe non supporta più richiami concorrenti, esporre una proprietà IsBusy. In questo modo gli sviluppatori possono stabilire se un metodo NomeMetodoAsync è in esecuzione senza intercettare un'eccezione generata dal metodo NomeMetodoAsync.

Rispettare la semantica della proprietà IsBusy illustrata in Suggerimenti per l'implementazione del modello asincrono basato su eventi.

Supporto facoltativo della generazione di report sullo stato di avanzamento

In numerosi casi è richiesta la generazione di report sullo stato di avanzamento di un'operazione asincrona. Nel modello asincrono basato su eventi sono disponibili le istruzioni necessarie in tal senso.

  • Facoltativamente definire un evento da generare durante l'operazione asincrona e da richiamare sul thread desiderato. L'oggetto ProgressChangedEventArgs supporta un indicatore di stato con valori interi che devono essere compresi tra 0 e 100.

  • Denominare l'evento come illustrato di seguito.

    • ProgressChanged se la classe supporta più operazioni asincrone o ne è previsto l'aumento delle dimensioni per includere più operazioni asincrone nelle versioni future.

    • NomeMetodoProgressChanged se la classe presenta una sola operazione asincrona.

    Questa scelta di denominazione è parallela a quella effettuata per il metodo di annullamento, come illustrato nella sezione Supporto facoltativo dell'annullamento.

Questo evento deve utilizzare la firma del delegato ProgressChangedEventHandler e la classe ProgressChangedEventArgs. In alternativa, se è disponibile un indicatore di stato più specifico del dominio, ad esempio con la segnalazione dei byte letti e di quelli totali per un'operazione di download, è necessario definire una classe derivata di ProgressChangedEventArgs.

Per la classe è presente un solo evento ProgressChanged o NomeMetodoProgressChanged, indipendentemente dal numero di metodi asincroni supportati. Per i client è previsto l'utilizzo dell'oggetto userState passato ai metodi NomeMetodoAsync per distinguere i diversi aggiornamenti dello stato di avanzamento in più operazioni simultanee.

È possibile che in alcune situazioni più operazioni supportino la funzionalità di avanzamento per la quale ognuna restituisce un indicatore diverso. In tal caso, non è sufficiente un solo evento ProgressChanged. Si consiglia di utilizzare un numero maggiore di eventi di questo tipo e di applicare il modello di denominazione NomeMetodoProgressChanged per ogni metodo NomeMetodoAsync.

Rispettare la semantica per la generazione di report sullo stato di avanzamento illustrata in Suggerimenti per l'implementazione del modello asincrono basato su eventi.

Supporto facoltativo della restituzione di risultati incrementali

A volte un'operazione asincrona può restituire risultati incrementali prima del completamento. Per il supporto di questo scenario sono disponibili diverse opzioni, alcune delle quali sono indicate di seguito.

Classe con singola operazione

Se una classe supporta una singola operazione asincrona e dall'operazione possono essere restituiti risultati incrementali:

  • Estendere il tipo ProgressChangedEventArgs per il supporto dei dati dei risultati incrementali e definire un evento NomeMetodoProgressChanged con i dati estesi.

  • Generare l'evento NomeMetodoProgressChanged in questione in presenza di un risultato incrementale da inserire nel report.

Questa soluzione si applica in modo specifico a una classe con singola operazione asincrona in quanto non determina alcun problema se lo stesso evento si ripete per restituire gli stessi risultati incrementali su tutte le operazioni, come avviene con l'evento NomeMetodoProgressChanged.

Classe con più operazioni che producono risultati incrementali omogenei

In questo caso, la classe supporta più metodi asincroni, ognuno dei quali in grado di restituire risultati incrementali che presentano tutti lo stesso tipo di dati.

Seguire il modello illustrato precedentemente per le classi con singola operazione, in quanto la stessa struttura EventArgs funzionerà con tutti i risultati incrementali. Definire un evento ProgressChanged, anziché un evento NomeMetodoProgressChanged, dal momento che si applica a più metodi asincroni.

Classe con più operazioni che producono risultati incrementali eterogenei

Se la classe supporta più metodi asincroni, ognuno dei quali restituisce un tipo di dati diverso, sarà necessario eseguire le operazioni riportate di seguito.

  • Separare il report del risultato incrementale da quello sullo stato di avanzamento.

  • Definire un evento NomeMetodoProgressChanged distinto con l'oggetto EventArgs appropriato per ciascun metodo asincrono in modo da gestire i dati dei risultati incrementali del metodo in questione.

Richiamare il gestore eventi specifico sul thread desiderato, come illustrato in Suggerimenti per l'implementazione del modello asincrono basato su eventi.

Gestione di parametri Out e Ref nei metodi

Sebbene generalmente in .NET Framework non sia consigliato utilizzare out e ref, di seguito sono riportate le regole da seguire qualora tali parametri siano presenti.

In presenza di un metodo NomeMetodo sincrono:

  • I parametri out per il metodo NomeMetodo non devono far parte di NomeMetodoAsync, ma di NomeMetodoCompletedEventArgs, con lo stesso nome del relativo parametro equivalente in NomeMetodo, a meno che non esista un nome più appropriato.

  • I parametri ref per il metodo NomeMetodo devono far parte di NomeMetodoAsync e di NomeMetodoCompletedEventArgs, con lo stesso nome del relativo parametro equivalente in NomeMetodo, a meno che non esista un nome più appropriato.

Ad esempio, dato il codice:

Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);

Il metodo asincrono e la relativa classe AsyncCompletedEventArgs saranno simili a quanto riportato di seguito.

Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)

Public Class MethodNameCompletedEventArgs
    Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As Integer 
    End Property
    Public ReadOnly Property Arg2() As String 
    End Property
    Public ReadOnly Property Arg3() As String 
    End Property
End Class
public void MethodNameAsync(string arg1, string arg2);

public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

Vedere anche

Attività

Procedura: implementare un componente che supporta il modello asincrono basato su eventi

Procedura: eseguire un'operazione in background

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

Concetti

Quando implementare il modello asincrono basato su eventi

Suggerimenti per l'implementazione del modello asincrono basato su eventi

Riferimenti

ProgressChangedEventArgs

AsyncCompletedEventArgs

Altre risorse

Programmazione multithreading con il modello asincrono basato su eventi