Condividi tramite


Gestione della reentrancy nelle applicazioni asincrone (C# e Visual Basic)

Quando si include codice asincrono nell'applicazione, è necessario considerare ed eventualmente impedire la reentrancy, ossia la reimmissione di un'operazione asincrona prima che sia completata. Se non si identificano e si gestiscono possibilità per la reentrancy, è possibile che vengano generati risultati imprevisti.

In questo argomento

Nota

Le istruzioni in Reviewing and Running the Example App mostrano come eseguire il codice come applicazione WPF (Windows Presentation Foundation) o come applicazione Windows Store.

Per eseguire l'esempio, come un'applicazione WPF, è necessario che Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 per Windows Desktop,Visual Studio Express 2013 per Windows o .NET Framework 4.5 o 4.5.1 siano installati sul computer.

Per eseguire l'esempio come un'applicazione Windows Store, è necessario che Windows 8 sia installato nel computer.Inoltre, se si desidera eseguire l'esempio da Visual Studio, è necessario che anche Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 per Windows Desktop, o Visual Studio Express 2013 per Windows siano installati.

Riconoscimento della reentrancy

Nell'esempio in questo argomento gli utenti scelgono un pulsante Avvia per avviare un'applicazione asincrona che scarica una serie di siti Web e calcola il numero totale di byte che vengano scaricati. Una versione sincrona dell'esempio risponderebbe allo stesso modo indipendentemente dal numero di volte in cui un utente seleziona il pulsante, perché dopo la prima volta, il thread dell'interfaccia utente ignora gli eventi finché l'applicazione non ha completato l'esecuzione. In un'applicazione asincrona, tuttavia, il thread UI continua a rispondere e sarebbe possibile all'operazione asincrona prima del completamento.

Nell'esempio seguente viene mostrato l'output previsto se l'utente fa clic solo una volta sul pulsante Avvia. Viene visualizzato un elenco di siti Web scaricati con la dimensione di ogni sito in byte. Il numero totale di byte è visualizzato alla fine.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Tuttavia, se l'utente sceglie più volte il pulsante, il gestore eventi viene chiamato ripetutamente e si torna ogni volta al processo di download. Pertanto, vengono eseguite contemporaneamente più operazioni asincrone, l'output esegue l'interfogliazione dei risultati e il numero totale di byte è fuorviante.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

È possibile esaminare il codice che produce questo output scorrendo alla fine di questo argomento. È possibile provare il codice scaricando la soluzione nel computer locale ed eseguendo il progetto WebsiteDownload oppure utilizzando il codice alla fine di questo argomento per creare il proprio progetto. Per ulteriori informazioni e istruzioni, vedere Esame ed esecuzione dell'applicazione di esempio.

Gestione della reentrancy

È possibile gestire la reentrancy in diversi modi, in base alle operazioni che si desiderano vengano eseguite dall'applicazione. Questo argomento presenta gli esempi seguenti:

  • Disabilitare il pulsante Start

    Disabilitare il pulsante Start mentre l'operazione viene eseguita in modo che l'utente non possa interromperla.

  • Annullare e riavviare l'operazione

    Annullare qualsiasi operazione ancora in esecuzione quando l'utente sceglie nuovamente il pulsante Start, quindi consentire la continuazione dell'operazione richiesta più di recente.

  • Eseguire più operazioni e mettere in coda l'output

    Consentire che tutte le operazioni richieste vengano eseguite in modo asincrono, ma coordinare la visualizzazione dell'output in modo che i risultati di ogni operazione vengano visualizzati insieme e in ordine.

Disabilitare il pulsante Start

È possibile bloccare il pulsante Start mentre un'operazione è in esecuzione disabilitando il pulsante nella parte superiore del gestore eventi StartButton_Click. È quindi possibile riabilitare il pulsante dall'interno di un blocco finally quando l'operazione viene completata in modo che gli utenti possano eseguire nuovamente l'applicazione.

Nel codice seguente vengono mostrate le modifiche, contrassegnate con l'asterisco. È possibile aggiungere le modifiche al codice alla fine di questo argomento oppure scaricare l'applicazione finita da Esempi asincroni: reentrancy nelle applicazioni desktop .NET o Esempi asincroni: reentrancy nelle applicazioni Windows Store. Il nome del progetto è DisableStartButton.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again. 
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Text = "";

    // ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = false; 

    try
    {
        await AccessTheWebAsync();
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
    // ***Enable the Start button in case you want to run the program again. 
    finally
    {
        StartButton.IsEnabled = true;
    }
}

A cause delle modifiche, il pulsante durante il caricamento dei siti Web da parte di AccessTheWebAsync non risponde, pertanto non è possibile tornare al processo.

Annullare e riavviare l'operazione

Anziché disabilitare il pulsante Start, è possibile mantenerlo attivo, ma se l'utente sceglie nuovamente il pulsante, viene annullata l'operazione già in esecuzione e consente all'operazione avviata più di recente di continuare.

Per ulteriori informazioni sull'annullamento, vedere la pagina relativa all' ottimizzazione dell'applicazione asincrona.

Per installare questo scenario, apportare le modifiche seguenti al codice di base fornito in Esame ed esecuzione dell'applicazione di esempio. È inoltre possibile scaricare l'applicazione finita da Esempi asincroni: reentrancy nelle applicazioni desktop .NET or Async Esempi: Reentrancy in applicazioni Windows Store. Il nome di questo progetto è CancelAndRestart.

  1. Dichiarare una variabile CancellationTokenSource, cts, che sta nell'ambito di tutti i metodi.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
    public partial class MainWindow : Window   // Or class MainPage
    {
        // *** Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;
    
  2. In StartButton_Click determinare se un'operazione è già in corso. Se il valore di cts è Null (Nothing in Visual Basic), nessuna operazione è già attiva. Se il valore non è Null, l'operazione già in esecuzione viene annullata.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }
    
  3. Impostare cts su un valore diverso che rappresenta il processo corrente.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
    // *** Now set cts to a new value that you can use to cancel the current process
    // if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;
    
  4. Alla fine di StartButton_Click, il processo corrente è completato, impostare quindi il valore di cts di nuovo su Null.

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    
    // *** When the process is complete, signal that another process can begin.
    if (cts == newCTS)
        cts = null;
    

Il codice seguente illustra tutti i cambiamenti in StartButton_Click. Le aggiunte sono contrassegnate con asterischi.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer. 
    'ResultsTextBox.Text = ""


    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)
        

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Clear();

    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }

    // *** Now set cts to cancel the current process if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;

    try
    {
        // ***Send cts.Token to carry the message if there is a cancellation request.
        await AccessTheWebAsync(cts.Token);
        
    }
    // *** Catch cancellations separately.
    catch (OperationCanceledException)
    {
        ResultsTextBox.Text += "\r\nDownloads canceled.\r\n";
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.\r\n";
    }
    // *** When the process is complete, signal that another process can proceed.
    if (cts == newCTS)
        cts = null;
}

Effettuare le seguenti modifiche in AccessTheWebAsync.

  • Aggiungere un parametro per accettare il token di annullamento da StartButton_Click.

  • Utilizzare il metodo GetAsync per scaricare i siti Web perché GetAsync accetta un argomento CancellationToken.

  • Prima di chiamare DisplayResults per visualizzare i risultati per ogni sito Web scaricato, controllare ct per verificare che l'operazione corrente non sia stata annullata.

Nel codice seguente vengono mostrate le modifiche, contrassegnate con l'asterisco.

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a 
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the 
        ' latest site. 
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
// *** Provide a parameter for the CancellationToken from StartButton_Click.
async Task AccessTheWebAsync(CancellationToken ct)
{
    // Declare an HttpClient object.
    HttpClient client = new HttpClient();

    // Make a list of web addresses.
    List<string> urlList = SetUpURLList();

    var total = 0;
    var position = 0;

    foreach (var url in urlList)
    {
        // *** Use the HttpClient.GetAsync method because it accepts a 
        // cancellation token.
        HttpResponseMessage response = await client.GetAsync(url, ct);

        // *** Retrieve the website contents from the HttpResponseMessage.
        byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

        // *** Check for cancellations before displaying information about the 
        // latest site. 
        ct.ThrowIfCancellationRequested();

        DisplayResults(url, urlContents, ++position);

        // Update the total.
        total += urlContents.Length;
    }

    // Display the total count for all of the websites.
    ResultsTextBox.Text +=
        string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
}   

Se si sceglie più volte il pulsante Avvia mentre l'applicazione è in esecuzione, verranno prodotti risultati simili al seguente output.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Per eliminare gli elenchi parziali, rimuovere il commento dalla prima riga di codice in StartButton_Click per deselezionare la casella di testo ogni volta che l'utente riavvia l'operazione.

Eseguire più operazioni e mettere in coda l'output

Il terzo esempio è il più complesso in quanto l'applicazione avvia un'altra operazione asincrona ogni volta che l'utente sceglie Start e tutte le operazioni vengono eseguite fino al completamento. Tutte le operazioni richieste consentono di scaricare i siti Web dall'elenco in modo asincrono, ma l'output delle operazioni viene presentato in modo sequenziale. Ovvero l'attività di download effettiva è interfogliata, come mostra l'output in Recognizing Reentrancy, ma l'elenco dei risultati per ogni gruppo viene presentato separatamente.

Le operazioni condividono un oggetto Task globale, pendingWork, che funge da gatekeeper per il processo di visualizzazione.

È possibile eseguire l'esempio inserendo le modifiche nel codice in Compilazione dell'applicazione o attenersi alle istruzioni in Download dell'applicazione per scaricare l'esempio ed eseguire il progetto QueueResults.

Nell'output seguente viene mostrato il risultato di un clic sul pulsante Avvia da parte dell'utente. L'etichetta con la lettera A indica che il risultato deriva dalla prima volta che è stato fatto clic sul pulsante Avvia. I numeri mostrano l'ordine degli URL nell'elenco delle destinazioni di download.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876


#Group A is complete.

Se l'utente sceglie il pulsante Avvia tre volte, l'applicazione genera un output analogo alle righe seguenti. Le righe informative precedute da un carattere di cancelletto (#) registrano lo stato di avanzamento dell'applicazione.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

I gruppi B e C iniziano prima che il gruppo A abbia terminato, ma l'output di ogni gruppo viene visualizzato separatamente. Tutto l'output per il gruppo A viene visualizzato per primo, seguito da tutto l'output per il gruppo B, quindi da tutto l'output per il gruppo C. L'applicazione visualizza sempre i gruppi in ordine e, per ciascun gruppo, visualizza sempre le informazioni sui singoli siti Web nell'ordine in cui gli URL appaiono nell'elenco degli URL.

Non è tuttavia possibile prevedere l'ordine in cui si verificano i download. Dopo aver avviato più gruppi, le attività di download che questi generano sono tutte attive. Non è possibile presupporre che A-1 venga scaricato prima di B-1, né che A-1 venga scaricato prima di A-2.

Definizioni globali

Il codice di esempio contiene le seguenti due dichiarazioni globali che sono visibili da tutti i metodi.

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.  
    Private pendingWork As Task = Nothing 
    Private group As Char = ChrW(AscW("A") - 1)
public partial class MainWindow : Window  // Class MainPage in Windows Store app.
{
    // ***Declare the following variables where all methods can access them.  
    private Task pendingWork = null;   
    private char group = (char)('A' - 1);

La variabile di Task, pendingWork, supervisiona il processo di visualizzazione e impedisce a qualsiasi gruppo di interrompere l'operazione di visualizzazione di un altro gruppo. La variabile di caratteri, group, etichetta l'output dei diversi gruppi per verificare che i risultati siano visualizzati nell'ordine previsto.

Il gestore eventi Click

Il gestore eventi, StartButton_Click, incrementa la lettera del gruppo ogni volta che l'utente sceglie il pulsante Avvia. Il gestore chiama quindi AccessTheWebAsync per eseguire l'operazione di download.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that 
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try 
        ' *** Pass the group value to AccessTheWebAsync. 
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and  
        ' display procedures. 
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed." 

    End Try 
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // ***Verify that each group's results are displayed together, and that 
    // the groups display in order, by marking each group with a letter. 
    group = (char)(group + 1);
    ResultsTextBox.Text += string.Format("\r\n\r\n#Starting group {0}.", group);

    try
    {
        // *** Pass the group value to AccessTheWebAsync. 
        char finishedGroup = await AccessTheWebAsync(group);

        // The following line verifies a successful return from the download and 
        // display procedures. 
        ResultsTextBox.Text += string.Format("\r\n\r\n#Group {0} is complete.\r\n", finishedGroup);
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
}

Il metodo AccessTheWebAsync

In questo esempio AccessTheWebAsync viene diviso in due metodi. Il primo metodo, AccessTheWebAsync, avvia tutte le attività di download per un gruppo e imposta pendingWork in modo da controllare il processo di visualizzazione. Il metodo utilizza una query LINQ (Language Integrated Query) e ToArray``1 per avviare tutte le attività di download contemporaneamente.

AccessTheWebAsync chiama quindi FinishOneGroupAsync per attendere il completamento di ogni download e visualizzarne la lunghezza.

FinishOneGroupAsync restituisce un'attività assegnata a pendingWork in AccessTheWebAsync. Il valore impedisce l'interruzione da un'altra operazione prima che l'attività venga completata.

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download. 
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks. 
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results. 
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return. 
    Return grp
End Function
private async Task<char> AccessTheWebAsync(char grp)
{
    HttpClient client = new HttpClient();

    // Make a list of the web addresses to download.
    List<string> urlList = SetUpURLList();

    // ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Task<byte[]>[] getContentTasks = urlList.Select(url => client.GetByteArrayAsync(url)).ToArray();

    // ***Call the method that awaits the downloads and displays the results. 
    // Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp);

    ResultsTextBox.Text += string.Format("\r\n#Task assigned for group {0}. Download tasks are active.\r\n", grp);

    // ***This task is complete when a group has finished downloading and displaying.
    await pendingWork;

    // You can do other work here or just return. 
    return grp;
}

Il metodo FinishOneGroupAsync

Questo metodo scorre le attività di download in un gruppo, attendendo ciascuna, visualizzando la lunghezza del sito Web scaricato e aggiungendo la lunghezza al totale.

La prima istruzione in FinishOneGroupAsync utilizza pendingWork per garantire che l'inserimento del metodo non interferisca con un'operazione già in fase di visualizzazione o già in attesa. Se tale operazione è in corso, l'operazione entrante deve attendere il proprio turno.

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results. 
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If 

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync. 
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and 
        ' its length. 
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next 

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
private async Task FinishOneGroupAsync(List<string> urls, Task<byte[]>[] contentTasks, char grp)
{
    // ***Wait for the previous group to finish displaying results. 
    if (pendingWork != null) await pendingWork;

    int total = 0;

    // contentTasks is the array of Tasks that was created in AccessTheWebAsync. 
    for (int i = 0; i < contentTasks.Length; i++)
    {
        // Await the download of a particular URL, and then display the URL and 
        // its length. 
        byte[] content = await contentTasks[i];
        DisplayResults(urls[i], content, i, grp);
        total += content.Length;
    }

    // Display the total count for all of the websites.
    ResultsTextBox.Text +=
        string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
}

È possibile eseguire l'esempio inserendo le modifiche nel codice in Compilazione dell'applicazione o attenersi alle istruzioni in Download dell'applicazione per scaricare l'esempio ed eseguire il progetto QueueResults.

Punti di interesse

Le righe informative precedute da un carattere di cancelletto (#) nell'output illustrano come funziona questo esempio.

Nell'output sono mostrati i seguenti pattern.

  • Un gruppo può essere avviato mentre un gruppo precedente sta visualizzando il relativo output, ma la visualizzazione dell'output del gruppo precedente non viene interrotta.

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • L'attività pendingWork è null (Nothing in Visual Basic) all'inizio di FinishOneGroupAsync solo per il gruppo A, il primo ad iniziare. Il gruppo A non ha ancora completato un'espressione await quando raggiunge FinishOneGroupAsync. Di conseguenza, il controllo non è stato restituito a AccessTheWebAsync e la prima assegnazione a pendingWork non si è verificata.

  • Le due righe seguenti vengono sempre visualizzate insieme nell'output. Il codice non viene mai interrotto tra l'avvio dell'operazione di un gruppo in StartButton_Click e l'assegnazione di un'attività per il gruppo a pendingWork.

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    Dopo l'accesso di un gruppo a StartButton_Click, l'operazione non completa un'espressione di attesa fino a quando l'operazione non accede a FinishOneGroupAsync. Pertanto, nessun'altra operazione può acquisire il controllo durante tale segmento di codice.

Esame ed esecuzione dell'applicazione di esempio

Per comprendere meglio l'applicazione di esempio, è possibile scaricarla, compilarla o esaminare il codice alla fine di questo argomento senza implementare l'applicazione.

Nota

Per eseguire l'esempio, come un'applicazione desktop Windows Presentation Foundation (WPF), è necessario che Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 per Windows Desktop,Visual Studio Express 2013 per Windows o .NET Framework 4.5 o 4.5.1 siano installati nel computer.

Per eseguire l'esempio come un'applicazione Windows Store, è necessario che Windows 8 sia installato nel computer.Inoltre, se si desidera eseguire l'esempio da Visual Studio, è necessario che Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 per Windows 8 o Visual Studio Express 2013 per Windows siano installati.Visual Studio 2010 non consente di caricare progetti destinati a .NET Framework 4.5.

Download dell'applicazione

  1. Scaricare il file compresso da Esempi asincroni: reentrancy nelle applicazioni desktop .NET o Esempi asincroni: Reentrancy nelle applicazioni Windows Store.

  2. Decomprimere il file scaricato, quindi avviare Visual Studio.

  3. Sulla barra dei menu scegliere File, Apri, Progetto/Soluzione.

  4. Passare alla cartella che contiene il codice di esempio decompresso e aprire il file di soluzione (con estensione sln).

  5. In Esplora soluzioni aprire il menu di scelta rapida per il progetto che si vuole eseguire, quindi scegliere Imposta come progetto di avvio.

  6. Digitare CTRIL + F5 per compilare ed eseguire il progetto.

Compilazione dell'applicazione

Nelle sezioni seguenti viene fornito il codice per compilare l'esempio come applicazione WPF o come applicazione Windows Store.

Per compilare un'applicazione WPF

  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 espandere Visual Basic o Visual C#, quindi espandere Windows.

  4. Nell'elenco dei tipi di progetto, scegliere Applicazione WPF.

  5. Assegnare il nome WebsiteDownloadWPF al progetto, quindi selezionare il pulsante OK.

    Il nuovo progetto verrà visualizzato in Esplora soluzioni.

  6. 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 soluzioni quindi scegliere Visualizza codice.

  7. Nel visualizzazione XAML di MainWindow.xaml, sostituire il codice con il seguente.

    <Window x:Class="MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    
    <Window x:Class="WebsiteDownloadWPF.MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    Una semplice finestra che contiene una casella di testo e un pulsante vengono visualizzati nella visualizzazione Progettazione di MainWindow.xaml.

  8. Aggiungere un riferimento per System.Net.Http.

  9. In Esplora soluzioni, aprire il menu di scelta rapida per MainWindow.xaml.vb o MainWindow.xaml.cs e scegliere Visualizza Codice.

  10. In MainWindow.xaml.vb o MainWindow.xaml.cs sostituire il codice con il seguente.

    ' Add the following Imports statements, and add a reference for System.Net.Http. 
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output. 
            'ResultsTextBox.Text = "" 
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed." 
    
            End Try 
        End Sub 
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object. 
            Dim client = New HttpClient()
    
            ' Make a list of web addresses. 
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task 
                ' produces a byte array. 
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next 
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function 
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function 
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed 
            ' to be used with a monospaced font, such as Lucida Console or 
            ' Global Monospace. 
    
            ' Strip off the "http:'". 
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub 
    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 the following using directives, and add a reference for System.Net.Http. 
    using System.Net.Http;
    using System.Threading;
    
    namespace WebsiteDownloadWPF
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output. 
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task 
                    // produces a byte array. 
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // Update the total.
                    total += urlContents.Length;
                }
    
                // Display the total count for all of the websites.
                ResultsTextBox.Text +=
                    string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
            }
    
    
            private List<string> SetUpURLList()
            {
                List<string> urls = new List<string> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // Display the length of each website. The string format is designed 
                // to be used with a monospaced font, such as Lucida Console or  
                // Global Monospace. 
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  11. Digitare CTRL + F5 per eseguire il programma, quindi scegliere il pulsante Avvia più volte.

  12. Apportare le modifiche da Disabilitare il pulsante Start, Annullare e riavviare l'operazione, o Eseguire più operazioni e mettere in coda l'output per gestire la reentrancy.

Per compilare un'applicazione Windows Store

  1. Avviare Visual Studio.

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

    Verrà visualizzata la finestra di dialogo Nuovo progetto.

  3. Nella categoria Installato, Modelli espandere Visual Basic o Visual C#, quindi espandere Windows Store.

  4. Nell'elenco dei tipi di progetto selezionare Applicazione vuota (XAML).

  5. Assegnare il nome WebsiteDownloadWin al progetto, quindi selezionare il pulsante OK.

    Il nuovo progetto verrà visualizzato in Esplora soluzioni.

  6. In Esplora soluzioni aprire il menu di scelta rapida per MainPage.xaml, quindi scegliere Apri.

  7. Nella finestra XAML di MainPage.xaml sostituire il codice con il seguente.

    <Page
        x:Class="WebsiteDownloadWin.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWin"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" FontSize="12">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="711"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" FontFamily="Lucida Console" />
        </Grid>
    </Page>
    

    Viene visualizzata una semplice finestra che contiene una casella di testo e un pulsante Start nella finestra Progettazione di MainPage.xaml.

  8. In Esplora soluzioni aprire il menu di scelta rapida per MainPage.xaml.vb o MainPage.xaml.cs, quindi scegliere Visualizza codice.

  9. Sostituire il codice in MainPage.xaml.vb o MainPage.xaml.cs con il seguente codice.

    ' Add the following Imports statements. 
    Imports System.Threading.Tasks
    Imports System.Threading
    Imports System.Net.Http
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        End Sub 
    
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output. 
            'ResultsTextBox.Text = "" 
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed." 
    
            End Try 
        End Sub 
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object. 
            Dim client = New HttpClient()
    
            ' Make a list of web addresses. 
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task 
                ' produces a byte array. 
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next 
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function 
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function 
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed 
            ' to be used with a monospaced font, such as Lucida Console or 
            ' Global Monospace. 
    
            ' Strip off the "http:'". 
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub 
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add the following using directives.  
    using System.Threading.Tasks;
    using System.Threading;
    using System.Net.Http;
    
    
    namespace WebsiteDownloadWin
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output. 
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task 
                    // produces a byte array. 
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // Update the total.
                    total += urlContents.Length;
                }
    
                // Display the total count for all of the websites.
                ResultsTextBox.Text +=
                    string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
            }
    
    
            private List<string> SetUpURLList()
            {
                List<string> urls = new List<string> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // Display the length of each website. The string format is designed 
                // to be used with a monospaced font, such as Lucida Console or 
                // Global Monospace. 
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  10. Digitare CTRL + F5 per eseguire il programma, quindi scegliere il pulsante Avvia più volte.

  11. Apportare le modifiche da Disabilitare il pulsante Start, Annullare e riavviare l'operazione, o Eseguire più operazioni e mettere in coda l'output per gestire la reentrancy.

Vedere anche

Attività

Procedura dettagliata: accesso al Web tramite Async e Await (C# e Visual Basic)

Concetti

Programmazione asincrona con Async e Await (C# e Visual Basic)

Altre risorse

Programmazione asincrona (applicazioni Windows Store)

Guida rapida: chiamata ad API asincrone in C# o Visual Basic