Flusso di controllo in programmi asincroni (C# e Visual Basic)
È possibile creare e gestire programmi asincroni più facilmente utilizzando le parole chiave Await e Async.Tuttavia, i risultati possono sorprendervi se non comprendete come il programma viene eseguito.Questo argomento traccia il flusso di controllo con un semplice programma asincrono per indicare quando il controllo passa da un metodo ad un altro e quali informazioni vengono trasferite ogni volta.
[!NOTA]
Le parole chiave di Await e di Async furono introdotte in Visual Studio 2012.Per ulteriori informazioni sulle nuove funzionalità della versione, vedere Novità di Visual Studio 2012.
In generale, è bene contrassegnate i metodi contenenti codice asincrono con il modificatore async (C#) o Async (Visual Basic).In un metodo contrassegnato con un modificatore async, è possibile utilizzare un operatore Await (Visual Basic) o un operatore await C#) per specificare dove il metodo deve essere messo in pausa per aspettare il completamento del un processo asincrono chiamante.Per ulteriori informazioni, vedere Programmazione asincrona con Async e Await (C# e Visual Basic).
Nell'esempio vengono utilizzati i metodi async per scaricare il contenuto di un sito Web specificato come stringa e visualizzarne la lunghezza.Nell'esempio seguente sono contenuti due metodi.
startButton_Click, che chiama AccessTheWebAsync e visualizza il risultato.
AccessTheWebAsync, che scarica il contenuto di un sito Web come stringa e ne restituisce la lunghezza.AccessTheWebAsync utilizza un metodo asincrono HttpClient, GetStringAsync(String), per scaricare il contenuto.
Le linee di proiezione numerate vengono visualizzate in punti strategici del programma per aiutarvi a capire come il programma viene eseguito e cosa si verifica in ogni punto contrassegnato.Le linee di proiezione sono contrassegnate da "UNA" a "SEI". Le etichette rappresentano l'ordine in cui il programma raggiunge queste righe di codice.
Il codice seguente illustrata 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 &=
String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
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://msdn.microsoft.com")
' THREE
Dim urlContents As String = Await getStringTask
' FIVE
Return urlContents.Length
End Function
End Class
public partial class MainWindow : Window
{
// . . .
private async void startButton_Click(object sender, RoutedEventArgs e)
{
// ONE
Task<int> getLengthTask = AccessTheWebAsync();
// FOUR
int contentLength = await getLengthTask;
// SIX
resultsTextBox.Text +=
String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
}
async Task<int> AccessTheWebAsync()
{
// TWO
HttpClient client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://msdn.microsoft.com");
// THREE
string urlContents = await getStringTask;
// FIVE
return urlContents.Length;
}
}
Ogni percorso contrassegnato, da "UNO" a "SEI", visualizza le informazioni sullo stato corrente del programma.Viene prodotto il seguente output.
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 utilizzato in questo argomento da MSDN, oppure compilarlo manualmente.
[!NOTA]
Per eseguire l'esempio, è necessario che Visual Studio 2012, Visual Studio express 2012, oppure .NET Framework 4,5 installato nel computer.
Scarica il Programma
È possibile scaricare l'applicazione per questo argomento da Esempio Async: Flusso di Controllo di Programmi Async.I passaggi seguenti aprono ed eseguono il programma.
Decomprimere il file scaricato e quindi avviare Visual Studio 2012.
Sulla barra dei menu scegliere File, Apri, Progetto/Soluzione.
Passare alla cartella che contiene il codice di esempio decompresso, aprire il file di soluzione (.sln) e quindi premere il tasto F5 per compilare ed eseguire il progetto.
Compilare il programma autonomamente
Il seguente progetto Windows Presentation Foundation (WPF) contiene l'esempio di codice per questo argomento.
Per eseguire il progetto, attenersi alla procedura descritta di seguito:
Avviare Visual Studio.
Nella barra del menu, scegliere File, Nuovo, Progetto.
Verrà visualizzata la finestra di dialogo Nuovo progetto.
Nel riquadro Modelli installati, scegliere Visual Basic o **Visual C#**quindi scegliere Applicazione WPF dall'elenco di tipi di progetto.
Immettere AsyncTracer come nome del progetto e quindi scegliere il pulsante OK.
Il nuovo progetto verrà visualizzato in Esplora soluzioni.
Nell'editor di codice di Visual Studio, scegliere la scheda MainWindow.xaml.
Se la scheda non è visibile, scegliere dal menu di scelta rapida per MainWindow.xaml in Esplora soluzioniquindi scegliere Visualizza codice.
Nel visualizzazione XAML di MainWindow.xaml, sostituire il codice con il seguente.
<Window xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://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>
<Window xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="AsyncTracer.MainWindow" Title="Control Flow Trace" Height="350" Width="592"> <Grid> <Button x:Name="startButton" Content="Start
" HorizontalAlignment="Left" Margin="250,10,0,0" VerticalAlignment="Top" Width="75" Height="24" Click="startButton_Click" d:LayoutOverrides="GridBox"/> <TextBox x:Name="resultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="576" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" Grid.ColumnSpan="3"/> </Grid> </Window>
Una semplice finestra che contiene una casella di testo e un pulsante vengono visualizzati nella visualizzazione Progettazione di MainWindow.xaml.
Aggiungere un riferimento per System.Net.Http.
In Esplora soluzioni, aprire il menu di scelta rapida per MainWindow.xaml.vb o MainWindow.xaml.cs e scegliere Visualizza Codice.
In MainWindow.xaml.vb o MainWindow.xaml.cs sostituire il codice con il seguente.
' 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://msdn.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
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; // Add a using directive and a reference for System.Net.Http; using System.Net.Http; namespace AsyncTracer { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { // The display lines in the example lead you through the control shifts. resultsTextBox.Text += "ONE: Entering startButton_Click.\r\n" + " Calling AccessTheWebAsync.\r\n"; Task<int> getLengthTask = AccessTheWebAsync(); resultsTextBox.Text += "\r\nFOUR: Back in startButton_Click.\r\n" + " Task getLengthTask is started.\r\n" + " About to await getLengthTask -- no caller to return to.\r\n"; int contentLength = await getLengthTask; resultsTextBox.Text += "\r\nSIX: Back in startButton_Click.\r\n" + " Task getLengthTask is finished.\r\n" + " Result from AccessTheWebAsync is stored in contentLength.\r\n" + " About to display contentLength and exit.\r\n"; resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength); } async Task<int> AccessTheWebAsync() { resultsTextBox.Text += "\r\nTWO: Entering AccessTheWebAsync."; // Declare an HttpClient object. HttpClient client = new HttpClient(); resultsTextBox.Text += "\r\n Calling HttpClient.GetStringAsync.\r\n"; // GetStringAsync returns a Task<string>. Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); resultsTextBox.Text += "\r\nTHREE: Back in AccessTheWebAsync.\r\n" + " Task getStringTask is started."; // AccessTheWebAsync can continue to work until getStringTask is awaited. resultsTextBox.Text += "\r\n About to await getStringTask and return a Task<int> to startButton_Click.\r\n"; // Retrieve the website contents when task is complete. string urlContents = await getStringTask; resultsTextBox.Text += "\r\nFIVE: Back in AccessTheWebAsync." + "\r\n Task getStringTask is complete." + "\r\n Processing the return statement." + "\r\n Exiting from AccessTheWebAsync.\r\n"; return urlContents.Length; } } }
Premere il tasto F5 per eseguire il programma, quindi scegliere il pulsante Avvia.
Dovrebbe comparire il seguente output.
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.
Traccia il Programma
Passaggi UNO e DUE
Le prime due righe di proiezione tracciano il percorso come startButton_Click chiama AccessTheWebAsynce AccessTheWebAsync chiama il metodo asincrono HttpClientGetStringAsync(String).Nell'immagine seguente vengono descritte le chiamate dal metodo.
Il tipo restituito di entrambi gli AccessTheWebAsync e client.GetStringAsync è Task<TResult>.Per AccessTheWebAsync, TResult è un integer.Per GetStringAsync, TResult è una stringa.Per ulteriori informazioni sui tipi restituiti dai metodi async, vedere Tipi restituiti asincroni (C# e Visual Basic).
Un'attività di ritorno di un metodo async restituisce un'istanza quando il controllo torna al chiamante.Il controllo ritorna generalmente da un metodo async al suo chiamante quando un operatore await o Await sin incontra nel nel metodo chiamato o quando il metodo chiamato termina.Le linee di proiezione contrassegnate da "TRE" a "SEI" tracciano 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 restituisce da client.GetStringAsync a AccessTheWebAsync quando client.GetStringAsync viene restituito.
Il metodo client.GetStringAsync restituisce un'attività di stringa assegnata alla variabile getStringTask in AccessTheWebAsync.La riga seguente nel programma di esempio illustra la chiamata a client.GetStringAsync e l'assegnazione.
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
È possibile pensare all'attività come promessa per client.GetStringAsync in modo da produrre, eventualmente, una stringa effettiva.Contemporaneamente, se AccessTheWebAsync ha del lavoro da eseguire che non dipende dalla stringa promessa da client.GetStringAsync, tale lavoro può continuare finché client.GetStringAsync rimane in attesa.Nell'esempio, le seguenti righe di output, contrassegnate con "TRE," rappresentano la possibilità di eseguire il lavoro in modo indipendente
THREE: Back in AccessTheWebAsync.
Task getStringTask is started.
About to await getStringTask & return a Task<int> to startButton_Click.
La seguente istruzione sospende lo stato di avanzamento in AccessTheWebAsync quando getStringTask è atteso.
Dim urlContents As String = Await getStringTask
string urlContents = await getStringTask;
Nell'immagine seguente è illustrato il flusso di controllo da client.GetStringAsync all'assegnazione a getStringTask e dalla creazione getStringTask all'applicazione di un operatore di attesa.
L'await expression sospende AccessTheWebAsync fino al completamento di client.GetStringAsync.Contemporaneamente, restituisce il controllo al chiamante di AccessTheWebAsync, startButton_Click.
[!NOTA]
In genere, si attende la chiamata verso un metodo asincrono immediatamente.Ad esempio, una delle seguenti assegnazioni potrebbe sostituire il codice precedente e attende quindi getStringTask:
Visual Basic: Dim urlContents As String = Await client.GetStringAsync("https://msdn.microsoft.com")
C#: string urlContents = await client.GetStringAsync("https://msdn.microsoft.com");
In questo argomento, l'operatore di attesa viene applicato successivamente per inserire delle righe di output che marcano il flusso di controllo attraverso il programma.
Passaggio QUATTRO
Il tipo restituito dichiarato di AccessTheWebAsync è Task(Of Integer) in Visual Basic e in Task<int> in C#.Pertanto, quando AccessTheWebAsync viene sospeso, restituisce un'attività Integer a startButton_Click.È necessario comprendere che l'attività restituita non è getStringTask.L'attività restituita è una nuova attività del valore intero che rappresenta cosa rimane da eseguire nel metodo sospeso, AccessTheWebAsync.L'attività è una promessa di AccessTheWebAsync di produrre un Integer quando l'attività è completa.
La seguente istruzione assegna questa attività alla variabile getLengthTask.
Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
Task<int> getLengthTask = AccessTheWebAsync();
Come in AccessTheWebAsync, startButton_Click può continuare con il lavoro che non dipende dai risultati dell'attività asincrona (getLengthTask) finché l'attività rimane in attesa.Le seguenti righe di output rappresentano quel lavoro.
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 getLengthTask è in attesa.La seguente istruzione di assegnazione sospende startButton_Click finché AccessTheWebAsync non viene completato.
Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;
Nella figura seguente, le frecce mostrano il flusso di controllo dall'espressione di attesa in AccessTheWebAsync all'assegnazione di un valore a getLengthTask, seguita da una normale procedura in startButton_Click finché getLengthTask rimane in attesa.
Passaggio CINQUE
Quando client.GetStringAsync segnala che è completo, l'elaborazione in AccessTheWebAsync viene rilasciata dalla sospensione e può procedere oltre l'istruzione di attesa.Le seguenti righe di output rappresentano la ripresa di elaborazione.
FIVE: Back in AccessTheWebAsync.
Task getStringTask is complete.
Processing the return statement.
Exiting from AccessTheWebAsync.
L'operando dell'istruzione return, urlContents.Length, è archiviato nell'attività che AccessTheWebAsync restituisce.L'espressione di attesa recupera il valore da getLengthTask nel startButton_Click.
L'immagine seguente riporta il trasferimento di controllo dopo che client.GetStringAsync (e getStringTask) viene completato.
AccessTheWebAsync viene eseguita fino al completamento, e restituisce il controllo a startButton_Click, in attesa del completamento.
Passaggio SEI
Quando AccessTheWebAsync segnala di essere completo, l'esecuzione può continuare oltre l'istruzione di attesa in startButton_Async.Infatti, il programma non ha più altro da fare.
Le seguenti righe di output rappresentano la ripresa di 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 di attesa recupera da getLengthTask il numero intero che corrisponde all'operando dell'istruzione return contenuta in AccessTheWebAsync.La seguente istruzione assegna quel valore alla variabile contentLength.
Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;
Nella figura seguente viene riportata la restituzione del controllo da AccessTheWebAsync a startButton_Click.
Vedere anche
Attività
Procedura dettagliata: accesso al Web tramite Async e Await (C# e Visual Basic)
Procedura dettagliata: utilizzo del debugger con metodi Async
Concetti
Programmazione asincrona con Async e Await (C# e Visual Basic)
Tipi restituiti asincroni (C# e Visual Basic)
Altre risorse
Esempio Async: Flusso di Controllo in Programmi Async (C# e Visual Basic)