Udostępnij za pośrednictwem


Obsługa ponownego rozpoczęcia w aplikacjach asynchronicznych (C# i Visual Basic)

Po dołączeniu asynchronicznego kodu do aplikacji należy wziąć pod uwagę i ewentualnie zapobiec współużytkowaniu wątkowości, która odnosi się do ponownego wprowadzania operacji asynchronicznej, zanim została ona ukończona.Jeśli nie zidentyfikujesz i obsłużysz możliwości współużytkowania wątkowości, może to spowodować nieoczekiwane wyniki.

W tym temacie

[!UWAGA]

Instrukcje w Recenzja i uruchamianie aplikacji Przykład pokazują, jak uruchomić kod w formie aplikacji Windows Presentation Foundation (WPF) lub aplikacji Windows Store.

Aby uruchomić przykład jako aplikację WPF, na komputerze musisz mieć zainstalowane programy Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows lub .NET Framework w wersji 4.5 lub 4.5.1.

Aby uruchomić przykład jako aplikację Windows Store, musisz mieć zainstalowany na komputerze Windows 8.Ponadto, jeśli użytkownik chce uruchomić przykład z programu Visual Studio, musi także posiadać zainstalowane Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop lub Visual Studio Express 2013 for Windows.

Uznawanie współużytkowania wątkowości

W przykładzie w tym temacie użytkownicy wybierają przycisk Rozpocznij, aby zainicjować asynchroniczną aplikację do pobrania szeregu stron internetowych i obliczenia całkowitej liczby bajtów, które zostały pobrane.Synchroniczna wersja przykładu reaguje w taki sam sposób, bez względu na to, ile razy użytkownik wybierze przycisk, ponieważ po pierwszym uruchomieniu wątek interfejsu użytkownika ignoruje te zdarzenia, dopóki nie zakończy się uruchamianie aplikacji.Jednak w asynchronicznej aplikacji wątek interfejsu użytkownika w dalszym ciągu odpowiada i można ponownie wprowadzić operację asynchroniczną, zanim została ukończona.

Poniższy przykład ukazuje oczekiwane dane wyjściowe, jeśli użytkownik wybierze przycisk Start tylko raz.Zostanie wyświetlona lista pobranych stron internetowych z rozmiar, w bajtach, w każdym miejscu.Całkowita liczba bajtów pojawia się na końcu.

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

Jednakże jeśli użytkownik wybierze przycisk więcej niż jeden raz, program obsługi zdarzeń jest wywoływany kilkakrotnie i proces pobierania jest ponownie inicjowany każdorazowo.W rezultacie kilka operacji asynchronicznych jest uruchomionych w tym samym czasie, dane wyjściowe przeplatają wyniki i całkowita liczba bajtów jest niezrozumiała.

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

Możesz przeglądać kod, który generuje te dane wyjściowe przewijając do końca tego tematu.Możesz eksperymentować z kodem pobierając rozwiązanie na komputer lokalny, a następnie uruchamiając projekt WebsiteDownload, lub za pomocą kodu na końcu tego tematu, aby utworzyć własny projektu. Aby uzyskać więcej informacji oraz instrukcje, zobacz Recenzowanie i uruchamianie aplikacji przykładowych.

Obsługa współużytkowania wątkowości

Możesz obsługiwać współużytkowanie wątkowości na wiele sposobów, w zależności od tego, co ma robić Twoja aplikacja.Ten temat przedstawia następujące przykłady:

  • Wyłącz przycisk Start

    Wyłącz przycisk Start, gdy operacja jest uruchomiony, tak aby użytkownik nie mógł jej przerwać.

  • Anuluj i ponownie uruchom operację

    Anulować wszelkie operacje, które wciąż działają, gdy użytkownik wybierze ponownie przycisk Start, a następnie zezwól na kontynuowanie niedawno żądanej operacji.

  • Uruchom wiele operacji i zakolejkuj dane wyjściowe

    Zezwalaj wszystkim wymaganym operacjom na uruchamianie asynchronicznie, ale koordynuj wyniki z każdej operacji, aby były wyświetlane razem i w odpowiedniej kolejności.

Wyłącz przycisk Start

Możesz zablokować przycisk Start podczas trwania operacji przez wyłączenie przycisku u góry programu obsługi zdarzeń StartButton_Click.Następnie możesz ponownie włączyć przycisk od wewnątrz bloku finally po zakończeniu operacji, tak, aby użytkownicy mogli ponownie uruchomić aplikację.

Poniższy kod przedstawia te zmiany, które są oznaczone gwiazdkami.Możesz dodawać zmiany do kodu na końcu tego tematu, lub możesz pobierać gotowy formularz aplikacji z Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach pulpitu .NET lub Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach Sklepu Windows.Nazwa projektu to 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;
    }
}

W wyniku zmian przycisk przestaje odpowiadać, podczas gdy AccessTheWebAsync pobiera strony internetowe, więc procesy te nie mogą zostać ponownie wprowadzone.

Anuluj i ponownie uruchom operację

Zamiast wyłączać przycisk Start można zachować aktywny przycisk, jeśli użytkownik wybierze ten przycisk ponownie, anuluj operację, która jest już uruchomiona, i pozwól na kontynuowanie operacji ostatnio rozpoczętej.

Aby uzyskać więcej informacji o anulowaniu, zobacz Dostrajanie aplikacji asynchronicznej.

Aby skonfigurować ten scenariusz, należy wprowadzić następujące zmiany do kodu podstawowego, który znajduje się w Recenzowanie i uruchamianie aplikacji Przykład.Możesz również pobrać gotową aplikację z Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach pulpitu .NET lub Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach Sklepu Windows.Nazwa tego projektu to CancelAndRestart.

  1. Zadeklaruj zmienną CancellationTokenSource, cts, która znajduje się w zakresie dla wszystkich metod.

    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. W zdarzeniu StartButton_Click ustal, czy operacja jest już w toku.Jeśli wartość parametru cts jest null (Nothing w języku Visual Basic), żadna operacja nie jest już aktywna.Jeśli wartość nie jest null, operacja, która jest już uruchomiona, zostanie anulowana.

    ' *** 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. Ustaw cts na inną wartość, która reprezentuje bieżący proces.

    ' *** 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. Na koniec StartButton_Click bieżący proces się zakończy, więc ustaw wartość cts z powrotem na 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;
    

Poniższy kod pokazuje wszystkie zmiany w metodzie StartButton_Click.Dodatki są oznaczone gwiazdkami.

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;
}

W AccessTheWebAsync należy wprowadzić następujące zmiany:

  • Dodaj parametr do akceptowania tokenu odwołania z StartButton_Click.

  • Użyj metody GetAsync do pobierania witryn sieci Web, ponieważ GetAsync akceptuje argument CancellationToken.

  • Przed wywołaniem funkcji DisplayResults do wyświetlania wyników dla każdej pobranej witryny sieci Web, sprawdź ct, czy bieżąca operacja nie została anulowana.

Poniższy kod przedstawia te zmiany, które są oznaczone gwiazdkami.

' *** 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);
}   

Jeśli wybierzesz przycisk Start kilka razy, gdy ta aplikacja jest uruchomiona, powinno to dawać wyniki podobne do następujących danych wyjściowych.

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

Aby wyeliminować częściowe listy, usuń komentarz z pierwszego wiersza kodu w StartButton_Click, aby czyścić pole tekstowe zawsze, kiedy użytkownik ponownie uruchamia operację.

Uruchom wiele operacji i zakolejkuj dane wyjściowe

Trzeci przykład jest najbardziej skomplikowany, gdyż aplikacja rozpoczyna kolejną operację asynchroniczną za każdym razem, gdy użytkownik wybierze przycisk Start, a wszystkie operacje dobiegną końca.Wszystkie żądane operacje pobierają witryny sieci Web z listy asynchronicznie, ale dane wyjściowe operacji są przedstawiane sekwencyjnie.Oznacza to, że rzeczywiste działanie pobierania odbywa się z przeplotem, jak pokazuje wynik w Uznawanie współużytkowania wątkowości, ale lista wyników dla każdej grupy jest prezentowana oddzielnie.

Operacje współkorzystają z globalnego Task, pendingWork, który służy jako strażnik dla procesu wyświetlania.

Możesz uruchomić ten przykład, wklejając zmiany do kodu w Kompilowanie aplikacji, lub możesz postępować zgodnie z instrukcjami w Pobieranie aplikacji, aby pobrać próbkę a następnie uruchomić projekt QueueResults.

Poniższe dane wyjściowe ukazują wynik, jeśli użytkownik wybierze przycisk Start tylko raz.Etykieta A, wskazuje, że wynik dotyczy pierwszego wciśnięcia przycisku Start.Liczby pokazują kolejność adresów URL na liście elementów docelowych pobierania.

#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.

Jeśli użytkownik naciśnie przycisk Rozpocznij trzy razy, aplikacja generuje dane wyjściowe podobne do następujących wierszy.Linie informacyjne, rozpoczynające się od znaku funta (#) śledzą postęp aplikacji.

#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.

Grupy B i C rozpoczynają przed zakończeniem przez grupę A, ale dane wyjściowe dla każdej grupy pojawiają się oddzielnie.Wszystkie dane wyjściowe dla grupy A pojawiają się pierwsze, następnie wszystkie dane wyjściowe dla grupy B, a na koniec dla grupy C.Aplikacja zawsze wyświetla grupy w kolejności i, dla każdej grupy zawsze wyświetla informacje o poszczególnych witrynach sieci Web w kolejności, w jakiej adresy URL pojawiają się na liście adresów URL.

Jednak nie można przewidzieć kolejności, w której rzeczywiście wystąpi pobieranie.Po uruchomieniu wielu grup wszystkie zadania pobierania, które one generują, są aktywne.Nie można zakładać, że A-1 będą pobierane przed B 1, nie można też zakładać, że A-1 będą pobierane przed A-2.

Globalne definicje

Przykładowy kod zawiera poniższe dwie deklaracje globalne widoczne ze wszystkich metod.

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);

Zmienna Task, pendingWork, nadzoruje proces wyświetlania i zapobiega zakłócaniu pracy wyświetlania grupy przez jakąkolwiek inną grupę.Zmienna znaku, group, oznacza dane wyjściowe z różnych grup, aby sprawdzić, czy wyniki są wyświetlane w oczekiwanej kolejności.

Obsługa zdarzeń Kliknij

Program obsługi zdarzeń, StartButton_Click, stopniowo zwiększa literę grupy za każdym razem, gdy użytkownik wybiera przycisk Start.A następnie program obsługi zdarzeń wywołuje AccessTheWebAsync, aby uruchomić operację pobierania.

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.";
    }
}

Metoda AccessTheWebAsync

Ten przykład dzieli AccessTheWebAsync na dwie metody.Pierwsza metoda, AccessTheWebAsync, uruchamia wszystkie zadania pobierania dla grupy i konfiguruje pendingWork, aby kontrolować proces wyświetlania.Metoda wykorzystuje Language Integrated Query (zapytanie LINQ) i ToArray``1 do uruchomienia wszystkich zadań pobierania w tym samym czasie.

AccessTheWebAsync następnie wywołuje FinishOneGroupAsync, aby oczekiwać na zakończenie każdego pobrania i wyświetlić jego długość.

Metoda FinishOneGroupAsync zwraca zadanie, która jest przypisana do obiektu pendingWork w obiekcie AccessTheWebAsync.Ta wartość zapobiega przerywaniu przez inną operację przed zakończeniem zadania.

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;
}

Metoda FinishOneGroupAsync

Ta metoda przechodzi cyklicznie przez zadania pobierania w grupie, oczekując na każde z nich, wyświetlając długość pobranej witryny sieci Web i dodając długość do wyniku ogólnego.

Pierwsza instrukcja w FinishOneGroupAsync korzysta z pendingWork, aby upewnić się, że wprowadzenie metody nie koliduje z operacją, która już jest w trakcie wyświetlania, lub która już oczekuje.Jeśli taka operacja jest w toku, wchodząca operacja musi czekać na swoją kolej.

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);
}

Możesz uruchomić ten przykład, wklejając zmiany do kodu w Kompilowanie aplikacji, lub możesz postępować zgodnie z instrukcjami w Pobieranie aplikacji, aby pobrać próbkę, a następnie uruchomić projekt QueueResults.

Punkty orientacyjne

Linie informacyjne, rozpoczynające się od znaku funta (#) w danych wyjściowych wyjaśniają, jak działa ten przykład.

Dane wyjściowe pokazują następujące wzorce.

  • Grupę można uruchomić podczas wyświetlania danych wyjściowych poprzedniej grupy, ale wyświetlanie danych wyjściowych poprzedniej grupy nie zostanie przerwane.

    #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
    
  • Zadanie pendingWork ma wartość null (Nothing w języku Visual Basic) na początku FinishOneGroupAsync tylko dla grupy A, uruchomionej w pierwszej kolejności.Grupa A jeszcze nie zakończyła wykonywania wyrażenia oczekiwania, gdy dotarła do funkcji FinishOneGroupAsync.W związku z tym, formant nie wrócił do AccessTheWebAsync, a pierwsze przypisanie do pendingWork nie wystąpiło.

  • Następujące dwa wiersze zawsze pojawiają się razem w danych wyjściowych.Kod nigdy nie jest przerywany między rozpoczęciem operacji grupy w StartButton_Click i przypisywaniem zadania dla grupy do pendingWork.

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

    Po przejściu grupy do stanu StartButton_Click operacja nie kończy się i wyrażenie oczekuje, aż operacja wejdzie w stan FinishOneGroupAsync.Dlatego żadna inna operacja nie może przejmować kontroli w tym segmencie kodu.

Recenzowanie i uruchamianie aplikacji przykładowych

Aby lepiej zrozumieć przykładową aplikację, możesz ją pobrać, skompilować samodzielnie, lub przejrzeć kod na końcu tego tematu bez wdrażania aplikacji.

[!UWAGA]

Aby uruchomić przykład jako aplikację pulpitu Windows Presentation Foundation (WPF), na komputerze musisz mieć zainstalowane programy Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows lub .NET Framework w wersji 4.5 lub 4.5.1.

Aby uruchomić przykład jako aplikację Windows Store, musisz mieć zainstalowany na komputerze Windows 8.Ponadto, jeśli użytkownik chce uruchomić przykład z programu Visual Studio, musi także posiadać zainstalowane Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows 8 lub Visual Studio Express 2013 for Windows.Visual Studio 2010 nie może ładować projektów, które są przeznaczone dla .NET Framework 4.5.

Pobieranie aplikacji

  1. Pobierz skompresowany plik z Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach pulpitu .NET lub Próbki asynchroniczne: Współużytkowania wątkowości w aplikacjach Sklepu Windows.

  2. Przeprowadź dekompresję pobranego pliku, a następnie uruchom Visual Studio.

  3. Na pasku menu wybierz kolejno opcje Plik, Otwórz i Projekt/rozwiązanie.

  4. Przejdź do folderu, który posiada zdekompresowany przykładowy kod, a następnie otwórz plik rozwiązania (.sln).

  5. W oknie Eksplorator rozwiązań otwórz menu skrótów dla projektu, który chcesz uruchomić, a następnie wybierz polecenie Ustaw jako projekt startowy.

  6. Wybierz kombinację klawiszy CTRL+F5, aby skompilować i uruchomić projekt.

Kompilowanie aplikacji

W poniższych sekcjach przedstawiono kod, aby zbudować przykład taki jak aplikacja WPF lub Windows Store.

Aby utworzyć aplikację WPF

  1. Uruchom program Visual Studio.

  2. W pasku menu wybierz Plik, Nowy, Projekt.

    Zostanie otwarte okno dialogowe Nowy projekt.

  3. W okienku Zainstalowane szablony rozwiń węzeł Visual Basic lub Visual C#, a następnie rozwiń węzeł Windows.

  4. Z listy typów projektów wybierz Aplikacja WPF.

  5. Nazwij projekt WebsiteDownloadWPF, a następnie wybierz przycisk OK.

    W Eksploratorze rozwiązań pojawi się nowy projekt.

  6. W Edytorze koloru programu Visual Studio wybierz kartę MainWindow.xaml.

    Jeśli karta nie jest widoczna, otwórz menu skrótów dla pliku MainWindow.xaml w Eksploratorze rozwiązań, a następnie wybierz polecenie Wyświetl kod.

  7. W widoku XAML pliku MainWindow.xaml, zastąp kod następującym kodem.

    <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>
    

    Proste okno zawierające pole tekstowe i przycisk pojawia się w oknie Projekt MainWindow.xaml.

  8. Dodaj odwołanie do System.Net.Http.

  9. W Eksploratorze rozwiązań otwórz menu skrótów dla MainWindow.xaml.vb lub MainWindow.xaml.cs, a następnie wybierz polecenie Wyświetl kod.

  10. W MainWindow.xaml.vb lub MainWindow.xaml.cs, zastąp kod następującym kodem.

    ' 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. Wybierz kombinację klawiszy CTRL+F5, aby uruchomić program, a następnie kilkakrotnie wybierz przycisk Start.

  12. Wprowadź zmiany w opcjach Wyłączyć przycisk Start, Anuluj i ponownie uruchom operację lub Uruchom wiele operacji i umieść dane wyjściowe w kolejce do obsługi współużytkowania wątkowości.

Aby utworzyć aplikację Sklepu Windows

  1. Uruchom program Visual Studio.

  2. W pasku menu wybierz Plik, Nowy, Projekt.

    Zostanie otwarte okno dialogowe Nowy projekt.

  3. W kategorii Zainstalowane, Szablony rozwiń kolejno Visual Basic lub Visual C#, a następnie Sklep Windows.

  4. Na liście typów projektów wybierz Pusta aplikacja (XAML).

  5. Nazwij projekt WebsiteDownloadWin, a następnie wybierz przycisk OK.

    W Eksploratorze rozwiązań pojawi się nowy projekt.

  6. W Eksploratorze rozwiązań, otwórz menu skrótówna MainPage.xaml oraz wybierz polecenie Otwórz.

  7. W oknie XAML pliku MainPage.xaml, zastąp kod następujący kod.

    <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>
    

    Proste okno zawierające pole tekstowe i przycisk Start pojawia się w oknie Projekt MainPage.xaml.

  8. W Eksploratorze rozwiązań otwórz menu skrótów dla MainPage.xaml.vb lub MainPage.xaml.cs, a następnie wybierz polecenie Wyświetl kod.

  9. Zastąp kod w MainPage.xaml.vb lub MainPage.xaml.cs następującym kodem.

    ' 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. Wybierz kombinację klawiszy CTRL+F5, aby uruchomić program, a następnie kilkakrotnie wybierz przycisk Start.

  11. Wprowadź zmiany w opcjach Wyłączyć przycisk Start, Anuluj i ponownie uruchom operację lub Uruchom wiele operacji i umieść dane wyjściowe w kolejce do obsługi współużytkowania wątkowości.

Zobacz też

Zadania

Wskazówki: uzyskiwanie dostępu do sieci za pomocą Async i Await (C# i Visual Basic)

Koncepcje

Programowanie asynchroniczne z Async i Await (C# i Visual Basic)

Inne zasoby

Programowanie asynchroniczne (aplikacje Sklepu Windows)

Szybki Start: wywołanie asynchroniczne API w języku C# lub Visual Basic