Flusso di controllo in programmi asincroni (Visual Basic)

Le parole chiave Async e Await consentono di scrivere e gestire più facilmente i programmi asincroni. Tuttavia, i risultati potrebbero creare perplessità se non si conosce il funzionamento del programma. Questo argomento descrive il flusso di controllo attraverso un programma asincrono semplice per indicare quando il controllo si sposta da un metodo a un altro e quali informazioni vengono trasferite ogni volta.

Nota

Le parole chiave Async e Await sono state introdotte in Visual Studio 2012.

In generale, si contrassegnano i metodi che contengono codice asincrono con il modificatore Async. In un metodo contrassegnato con un modificatore async è possibile usare un operatore Await (Visual Basic) per specificare dove il metodo viene sospeso in attesa del completamento di un processo asincrono chiamato. Per altre informazioni, vedere Programmazione asincrona con Async e Await (Visual Basic).

L'esempio seguente usa i metodi asincroni per scaricare come stringa il contenuto di un sito Web specificato e per visualizzare la lunghezza della stringa. L'esempio contiene i due metodi seguenti.

  • startButton_Click, che chiama AccessTheWebAsync e visualizza il risultato.

  • AccessTheWebAsync, che scarica il contenuto di un sito Web come stringa e restituisce la lunghezza della stringa. AccessTheWebAsync usa un metodo HttpClient asincrono, ovvero GetStringAsync(String), per scaricare il contenuto.

In corrispondenza dei punti strategici del programma sono visualizzate righe numerate che consentono di comprendere come viene eseguito il programma e spiegano che cosa accade in ogni punto contrassegnato. Le righe sono evidenziate con etichette numerate da "ONE" a "SIX." Le etichette rappresentano l'ordine in cui il programma raggiunge queste righe di codice.

Il codice seguente rappresenta una struttura del programma.

Class MainWindow

    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click

        ' ONE
        Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

        ' FOUR
        Dim contentLength As Integer = Await getLengthTask

        ' SIX
        ResultsTextBox.Text &=
            vbCrLf & $"Length of the downloaded string: {contentLength}." & vbCrLf

    End Sub

    Async Function AccessTheWebAsync() As Task(Of Integer)

        ' TWO
        Dim client As HttpClient = New HttpClient()
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com")

        ' THREE
        Dim urlContents As String = Await getStringTask

        ' FIVE
        Return urlContents.Length
    End Function

End Class

Ognuna delle posizioni con etichetta da "ONE" a "SIX" visualizza informazioni sullo stato corrente del programma. Viene prodotto l'output seguente:

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

Impostare il programma

È possibile scaricare il codice usato in questo argomento da MSDN oppure crearlo manualmente.

Nota

Per eseguire l'esempio, è necessario che nel computer siano installati Visual Studio 2012 o versioni successive e .NET Framework 4.5 o versioni successive.

Scaricare il programma

È possibile scaricare l'applicazione di questo argomento da Async Sample: Control Flow in Async Programs (Esempio di attività asincrona: flusso di controllo in programmi asincroni). I passaggi seguenti consentono di aprire ed eseguire il programma.

  1. Decomprimere il file scaricato e quindi avviare Visual Studio.

  2. Nella barra dei menu scegliere File, Apri, Progetto/Soluzione.

  3. Passare alla cartella che contiene il codice di esempio decompresso, aprire il file della soluzione (SLN) e quindi premere F5 per compilare ed eseguire il progetto.

Compilare il programma autonomamente

Il seguente progetto Windows Presentation Foundation (WPF) contiene gli esempi di codice per questo argomento.

Per eseguire il progetto, effettuare i passaggi seguenti:

  1. Avviare Visual Studio.

  2. Nella barra dei menu scegliere File, Nuovo, Progetto.

    Verrà visualizzata la finestra di dialogo Nuovo progetto .

  3. Nel riquadro Modelli installati, scegliere Visual Basic e quindi scegliere Applicazione WPF nell'elenco dei tipi di progetto.

  4. Immettere AsyncTracer come nome del progetto e scegliere OK.

    Il nuovo progetto verrà visualizzato in Esplora soluzioni.

  5. Nell'Editor di codice di Visual Studio scegliere la scheda MainWindow.xaml .

    Se la scheda non è visibile, aprire il menu di scelta rapida per MainWindow.xaml in Esplora soluzioni e scegliere Visualizza codice.

  6. Nella visualizzazione XAML di MainWindow.xaml sostituire il codice con quello riportato di seguito.

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
        Title="Control Flow Trace" Height="350" Width="525">
        <Grid>
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/>
    
        </Grid>
    </Window>
    

    Nella visualizzazione Progettazione di MainWindow.xaml viene visualizzata una finestra semplice contenente una casella di testo e un pulsante.

  7. Aggiunge un riferimento a System.Net.Http.

  8. In Esplora soluzioni aprire il menu di scelta rapida per MainWindow.xaml.vb e quindi scegliere Visualizza codice.

  9. Sostituire il codice in MainWindow.xaml.vb con quello riportato di seguito.

    ' Add an Imports statement and a reference for System.Net.Http.
    Imports System.Net.Http
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    
            ' The display lines in the example lead you through the control shifts.
            ResultsTextBox.Text &= "ONE:   Entering StartButton_Click." & vbCrLf &
                "           Calling AccessTheWebAsync." & vbCrLf
    
            Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
    
            ResultsTextBox.Text &= vbCrLf & "FOUR:  Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is started." & vbCrLf &
                "           About to await getLengthTask -- no caller to return to." & vbCrLf
    
            Dim contentLength As Integer = Await getLengthTask
    
            ResultsTextBox.Text &= vbCrLf & "SIX:   Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is finished." & vbCrLf &
                "           Result from AccessTheWebAsync is stored in contentLength." & vbCrLf &
                "           About to display contentLength and exit." & vbCrLf
    
            ResultsTextBox.Text &=
                String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
        End Sub
    
        Async Function AccessTheWebAsync() As Task(Of Integer)
    
            ResultsTextBox.Text &= vbCrLf & "TWO:   Entering AccessTheWebAsync."
    
            ' Declare an HttpClient object.
            Dim client As HttpClient = New HttpClient()
    
            ResultsTextBox.Text &= vbCrLf & "           Calling HttpClient.GetStringAsync." & vbCrLf
    
            ' GetStringAsync returns a Task(Of String).
            Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")
    
            ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf &
                "           Task getStringTask is started."
    
            ' AccessTheWebAsync can continue to work until getStringTask is awaited.
    
            ResultsTextBox.Text &=
                vbCrLf & "           About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf
    
            ' Retrieve the website contents when task is complete.
            Dim urlContents As String = Await getStringTask
    
            ResultsTextBox.Text &= vbCrLf & "FIVE:  Back in AccessTheWebAsync." &
                vbCrLf & "           Task getStringTask is complete." &
                vbCrLf & "           Processing the return statement." &
                vbCrLf & "           Exiting from AccessTheWebAsync." & vbCrLf
    
            Return urlContents.Length
        End Function
    
    End Class
    
  10. Premere il tasto F5 per eseguire il programma e quindi scegliere il pulsante Start .

    Dovrebbe venire visualizzato l'output seguente:

    ONE:   Entering startButton_Click.
               Calling AccessTheWebAsync.
    
    TWO:   Entering AccessTheWebAsync.
               Calling HttpClient.GetStringAsync.
    
    THREE: Back in AccessTheWebAsync.
               Task getStringTask is started.
               About to await getStringTask & return a Task<int> to startButton_Click.
    
    FOUR:  Back in startButton_Click.
               Task getLengthTask is started.
               About to await getLengthTask -- no caller to return to.
    
    FIVE:  Back in AccessTheWebAsync.
               Task getStringTask is complete.
               Processing the return statement.
               Exiting from AccessTheWebAsync.
    
    SIX:   Back in startButton_Click.
               Task getLengthTask is finished.
               Result from AccessTheWebAsync is stored in contentLength.
               About to display contentLength and exit.
    
    Length of the downloaded string: 33946.
    

Analizzare il programma

Passaggi UNO e DUE

Le prime due righe di visualizzazione tracciano il percorso poiché startButton_Click chiama AccessTheWebAsync e AccessTheWebAsync chiama il metodo asincrono HttpClient, ovvero GetStringAsync(String). Nell'immagine seguente vengono illustrate le chiamate metodo a metodo.

Steps ONE and TWO

Il tipo restituito di AccessTheWebAsync e client.GetStringAsync è Task<TResult>. Per AccessTheWebAsync TResult è un numero intero. Per GetStringAsync TResult è una stringa. Per altre informazioni sui tipi restituiti dei metodi asincroni, vedere Tipi restituiti asincroni (Visual Basic).

Un metodo asincrono che restituisce un'attività restituisce un'istanza dell'attività quando il controllo torna al chiamante. Il controllo viene restituito da un metodo asincrono al relativo chiamante quando viene rilevato un operatore Await nel metodo chiamato o quando termina il metodo chiamato. Le righe evidenziate con le etichette da "THREE" a "SIX" analizzano questa parte del processo.

Passaggio TRE

In AccessTheWebAsync, il metodo asincrono GetStringAsync(String) viene chiamato per scaricare il contenuto della pagina Web di destinazione. Il controllo viene restituito da client.GetStringAsync a AccessTheWebAsync quando viene restituito client.GetStringAsync.

Il metodo client.GetStringAsync restituisce un'attività di stringa che viene assegnata alla variabile getStringTask in AccessTheWebAsync. La riga seguente nel programma di esempio indica la chiamata a client.GetStringAsync e l'assegnazione.

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")

Si può pensare all'attività come a una promessa fatta da client.GetStringAsync di produrre una stringa reale alla fine. Nel frattempo, se AccessTheWebAsync ha del lavoro da svolgere che non dipende dalla stringa promessa da client.GetStringAsync, il lavoro può continuare mentre client.GetStringAsync rimane in attesa. Nell'esempio, le righe di output seguenti, che sono contrassegnate con "THREE", rappresentano la possibilità di eseguire operazioni indipendenti

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

L'istruzione seguente sospende lo stato di avanzamento in AccessTheWebAsync quando si attende getStringTask.

Dim urlContents As String = Await getStringTask

L'immagine che segue illustra il flusso di controllo da client.GetStringAsync all'assegnazione a getStringTask e dalla creazione di getStringTask all'applicazione di un operatore Await.

Step THREE

L'espressione await sospende AccessTheWebAsync finché non viene restituito client.GetStringAsync. Nel frattempo il controllo viene restituito al chiamante di AccessTheWebAsync, startButton_Click.

Nota

In genere, la chiamata a un metodo asincrono si attende immediatamente. Ad esempio, l'assegnazione seguente potrebbe sostituire il codice precedente che crea e quindi attende getStringTask:Dim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")

In questo argomento l'operatore await viene applicato in un secondo tempo per contenere le righe di output che indicano il flusso di controllo attraverso il programma.

Passaggio QUATTRO

Il tipo restituito dichiarato di AccessTheWebAsync è Task(Of Integer). Di conseguenza, quando AccessTheWebAsync è sospeso, restituisce un'attività di valori integer a startButton_Click. È necessario comprendere che l'attività restituita non è getStringTask. L'attività restituita è una nuova attività di valori integer che rappresenta ciò che resta da eseguire nel metodo sospeso, AccessTheWebAsync. L'attività è una promessa da parte di AccessTheWebAsync di produrre un numero intero al termine dell'attività.

L'istruzione seguente assegna questa attività alla variabile getLengthTask.

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

Come in AccessTheWebAsync, startButton_Click può continuare a eseguire operazioni che non dipendono dai risultati dell'attività asincrona (getLengthTask) finché si è in attesa dell'attività. Le seguenti righe di output rappresentano tali operazioni:

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

Lo stato di avanzamento in startButton_Click viene sospeso quando si attende getLengthTask. La seguente istruzione di assegnazione sospende startButton_Click finché non si completa AccessTheWebAsync.

Dim contentLength As Integer = Await getLengthTask

Nella figura seguente le frecce indicano il flusso di controllo dall'espressione await in AccessTheWebAsync all'assegnazione di un valore a getLengthTask, seguita dall'elaborazione normale in startButton_Click finché si è in attesa di getLengthTask.

Step FOUR

Passaggio CINQUE

Quando l'attività client.GetStringAsync segnala il proprio completamento, l'elaborazione in AccessTheWebAsync viene rilasciata dalla sospensione e può continuare oltre l'istruzione await. Le seguenti righe di output rappresentano la ripresa dell'elaborazione:

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

L'operando dell'istruzione return, urlContents.Length, viene archiviato nell'attività restituita da AccessTheWebAsync. L'espressione await recupera tale valore da getLengthTask in startButton_Click.

L'immagine seguente illustra il trasferimento del controllo dopo il completamento di client.GetStringAsync (e getStringTask).

Step FIVE

AccessTheWebAsync viene eseguita fino al completamento e il controllo viene restituito a startButton_Click, che è in attesa del completamento.

Passaggio SEI

Quando l'attività AccessTheWebAsync segnala il proprio completamento, l'elaborazione può continuare oltre l'istruzione await in startButton_Async. Di fatto il programma non ha altre operazioni da eseguire.

Le seguenti righe di output rappresentano la ripresa dell'elaborazione in startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

L'espressione await recupera da getLengthTask il valore integer che rappresenta l'operando dell'istruzione return in AccessTheWebAsync. L'istruzione seguente assegna tale valore alla variabile contentLength.

Dim contentLength As Integer = Await getLengthTask

La figura seguente illustra la restituzione del controllo da AccessTheWebAsync a startButton_Click.

Step SIX

Vedi anche