다음을 통해 공유


비동기 응용 프로그램에서 재진입 처리(C# 및 Visual Basic)

응용 프로그램에 비동기 코드를 포함할 경우 비동기 작업이 완료되기 전에 다시 입력하는 재입력을 가능하면 방지하도록 고려해야 합니다. 재진입 가능성을 확인하고 처리하지 않은 경우 예기치 않은 결과가 발생할 수 있습니다.

항목 내용

참고

예제 앱 검토 및 실행에 나와 있는 지침은 코드를 WPF(Windows Presentation Foundation) 앱 또는 Windows 스토어 앱 중 하나로 실행하는 방법을 설명합니다.

예제를 WPF 응용 프로그램으로 실행하려면 Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows 또는 .NET Framework 4.5나 4.5.1이 컴퓨터에 설치되어 있어야 합니다.

예제를 Windows 스토어 응용 프로그램으로 실행하려면 컴퓨터에 Windows 8이 설치되어 있어야 합니다.또한 Visual Studio에서 예제를 실행하려면 Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop 또는 Visual Studio Express 2013 for Windows가 설치되어 있어야 합니다.

재진입 인식

이 항목의 예제에서는 사용자가 시작 단추를 선택하여 일련의 웹 사이트를 다운로드하고 다운로드한 총 바이트 수를 계산하는 비동기 응용 프로그램을 시작합니다. 예제의 동기화 버전은 처음 이후에는 응용 프로그렘 실행이 완료될 때까지 UI 스레드가 이러한 이벤트를 무시하므로 사용자가 단추를 몇 번 선택했는지에 관계없이 같은 방식으로 응답할 것입니다. 그러나 비동기 응용 프로그램에서 UI 스레드는 계속 응답하며 완료되기 전에 비동기 작업을 다시 시작할 수 있습니다.

다음 예제에서는 사용자가 시작 단추를 한 번만 선택하는 경우 예상되는 출력을 보여줍니다. 바이트로 표기한 각 사이트의 크기와 함께 웹 사이트의 목록이 나타납니다. 마지막에 총 바이트 수가 표시됩니다.

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

그러나 사용자가 단추를 두 번 이상 선택하는 경우 이벤트 처리기가 반복적으로 호출되고 다운로드 프로세스가 매번 다시 시작됩니다. 따라서 여러 비동기 작업이 동시에 실행 중이고 출력이 결과를 인터리브하며 총 바이트 수가 명확하지 않습니다.

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

이 항목의 끝으로 스크롤해서 이 출력을 생성하는 코드를 검토할 수 있습니다. 코드를 테스트하기 위해 솔루션을 로컬 컴퓨터에 다운로드한 다음, WebsiteDownload 프로젝트를 실행하거나 이 항목의 끝에 나오는 코드를 사용하여 고유의 프로젝트를 만들 수 있습니다. 자세한 내용 및 지침은 예제 응용 프로그램 검토 및 실행을 참조하십시오.

재진입 처리

응용 프로그램을 통해 수행하려는 작업에 따라 재진입을 다양한 방법으로 처리할 수 있습니다. 이 항목에서는 다음과 같은 예제가 제공됩니다.

시작 단추 사용 안 함

작업이 실행되는 동안에도 StartButton_Click 이벤트 처리기의 맨 위에 있는 단추를 비활성화하여 시작 단추를 차단할 수 있습니다. 사용자가 응용 프로그램을 다시 실행할 수 있도록 해당 작업이 완료되면 finally 블록에서 단추를 다시 활성화할 수 있습니다.

다음 코드에서는 별표로 표시된 이러한 변경 내용을 보여줍니다. 이 항목의 끝 부분에서 코드에 변경을 적용하거나 Async Samples: Reentrancy in .NET Desktop Apps 또는 Async Samples: Reentrancy in Windows Store Apps에서 완성된 응용 프로그램을 다운로드할 수 있습니다. 프로젝트 이름은 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;
    }
}

변경의 결과로 웹 사이트로 AccessTheWebAsync를 다운로드하는 동안 단추가 응답하지 않으므로 프로세스가 재진입할 수 없습니다.

작업 취소 및 다시 시작

시작 단추를 비활성화하는 대신 단추를 활성화하지만 사용자가 해당 단추를 다시 선택하는 경우 이미 실행 중인 작업을 취소하고 가장 최근에 시작한 작업을 계속할 수 있습니다.

취소에 대한 자세한 내용은 비동기 응용 프로그램 미세 조정을 참조하십시오.

이 시나리오를 설정하고 예제 응용 프로그램 검토 및 실행에 제공된 기본 코드에 다음 변경 내용을 적용합니다. 완성된 응용 프로그램을 Async Samples: Reentrancy in .NET Desktop Apps 또는 Async Samples: Reentrancy in Windows Store Apps에서 다운로드할 수 있습니다. 이 프로젝트의 이름은 CancelAndRestart입니다.

  1. 모든 메서드의 범위에 속하는 CancellationTokenSource 변수인 cts를 선언합니다.

    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. StartButton_Click에서 작업이 이미 진행 중인지 여부를 확인합니다. cts의 값이 null인 경우(Visual Basic의 Nothing) 이미 활성화된 작업이 없습니다. 값이 null인 경우 이미 실행 중인 작업이 취소됩니다.

    ' *** 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. 현재 프로세스를 나타내는 다른 값으로 cts 설정

    ' *** 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. StartButton_Click의 끝에서 현재 프로세스가 완료되므로 cts의 값이 다시 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;
    

다음 코드에서는 StartButton_Click의 모든 변경 내용을 보여줍니다. 추가 작업은 별표로 표시됩니다.

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

AccessTheWebAsync에서 다음과 같이 변경합니다.

  • 매개 변수를 추가하여 StartButton_Click에서 취소 토큰을 허용합니다.

  • GetAsync에서 CancellationToken 인수를 허용하므로 GetAsync 메서드를 사용하여 웹 사이트를 다운로드합니다.

  • 다운로드한 각 웹 사이트에 대한 결과를 표시하기 위해 DisplayResults을 호출하기 전에 ct에서 현재 작업이 취소되지 않았는지 확인합니다.

다음 코드에서는 별표로 표시된 이러한 변경 내용을 보여줍니다.

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

이 응용 프로그램이 실행되고 있는 동안 시작 단추를 여러 번 선택하는 경우 다음 출력과 비슷한 결과를 생성해야 합니다.

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

부분 목록을 제거하려면 StartButton_Click에서 코드 첫 번째 줄의 주석 처리를 제거하여 사용자가 작업을 다시 시작할 때마다 텍스트 상자를 지우도록 합니다.

여러 작업을 실행하고 출력을 큐 대기

세 번째 예제는 사용자가 시작 단추를 선택할 때마다 응용 프로그램이 다른 비동기 작업을 시작하고 모든 작업이 완료될 때까지 실행된다는 점에서 가장 복잡합니다. 요청된 모든 작업이 비동기적으로 목록에서 웹 사이트로 다운로드되지만 작업의 출력은 순차적으로 제공됩니다. 즉, 재진입 인식의 출력이 표시되지만 각 그룹의 결과 목록은 별도로 표시되므로 실제 다운로드 활동은 인터리브됩니다.

작업은 전역 Task, pendingWork를 공유하며 이는 표시 프로세스에서 게이트키퍼 역할을 합니다.

응용 프로그램 빌드에서 코드에 변경 내용을 붙여 넣어 이 예제를 실행하거나 앱 다운로드의 지시에 따라 샘플을 다운로드한 다음 QueueResults 프로젝트를 실행할 수 있습니다.

다음 출력은 사용자가 시작 단추를 한 번만 선택하는 경우의 결과를 보여줍니다. 문자 레이블 A는 시작 단추를 처음 선택한 경우의 결과임을 나타냅니다. 다운로드 대상 목록에서 URL의 순서를 표시하는 숫자입니다.

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

사용자가 시작 단추를 세 번 선택하는 경우 응용 프로그램은 다음 링크와 비슷한 출력을 생성합니다. 파운드 표시(#)으로 시작 하는 정보 줄은 응용 프로그램의 진행률을 추적합니다.

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

그룹 A가 완료되기 전에 B와 C가 시작되지만 각 그룹의 출력은 개별적으로 나타납니다. 그룹 A에 대한 모든 출력이 먼저 나타나고 그룹 B에 대한 모든 출력, 그룹 C에 대한 모든 출력이 이어서 나타납니다. 응용 프로그램은 항상 그룹을 순서대로 표시하며 각 그룹에 대해 항상 URL 목록에서 URL이 나타나는 순서로 개별 웹 사이트에 대한 정보를 표시합니다.

그러나 다운로드가 실제로 발생하는 순서를 예측할 수 없습니다. 여러 그룹을 시작한 후에는 생성되는 다운로드 작업은 모든 활성입니다. A-1이 B-1보다 먼저 다운로드된다고 가정할 수 없으며, A-1이 A-2보다 먼저 다운로드된다고 가정할 수 없습니다.

전역 정의

샘플 코드에 모든 방법에서 볼 수 있는 다음 두 개의 전역 선언을 포함합니다.

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

Task 변수, pendingWork는 표시 프로세스를 감독하고 그룹이 다른 그룹의 표시 작업을 중단하지 않도록 합니다. 문자 변수인 group은 결과가 예상된 순서로 표시되는지 확인하기 위해 다른 그룹의 출력에 레이블을 지정합니다.

Click 이벤트 처리기

이벤트 처리기, StartButton_Click, 사용자가 시작 단추를 선택할 때마다 증가된 그룹 문자입니다. 그런 다음 처리기에서 AccessTheWebAsync을 호출하여 다운로드 작업을 실행합니다.

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

AccessTheWebAsync 메서드

이 예제는 AccessTheWebAsync를 두 개의 메서드로 분할합니다. 첫 번째 메서드인 AccessTheWebAsync는 그룹에 대한 모든 다운로드 작업을 시작하고 pendingWork를 설정하여 표시 프로세스를 제어합니다. 이 메서드는 LINQ 쿼리(Language Integrated Query) 및 ToArray``1을 사용하여 모든 다운로드 작업이 동시에 시작되도록 합니다.

AccessTheWebAsync 그런 다음 FinishOneGroupAsync을 호출하여 각 다운로드가 완료되고 길이가 표시되길 기다립니다.

FinishOneGroupAsync은 AccessTheWebAsync의 pendingWork에 할당된 작업을 반환합니다 이 값은 작업이 완료되기 전에 다른 작업에 의한 중단을 방지합니다.

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

FinishOneGroupAsync 메서드

이 메서드는 그룹의 다운로드 작업에서 순환하며 각 작업을 기다리고, 다운로드한 웹 사이트의 길이를 표시하며, 전체에 해당 길이를 추가합니다.

FinishOneGroupAsync의 첫 번째 문은 pendingWork를 사용하여 메서드 입력이 표시 프로세스에 있거나 이미 대기 중인 작업을 방해하지 않도록 합니다. 이러한 작업이 진행 중인 경우 입력하는 작업이 차례를 기다려야 합니다.

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

응용 프로그램 빌드에서 코드에 변경 내용을 붙여 넣어 이 예제를 실행하거나 앱 다운로드의 지시에 따라 샘플을 다운로드한 다음 QueueResults 프로젝트를 실행할 수 있습니다.

관심 영역

출력에서 파운드 기호(#)로 시작하는 정보 줄은 이 예제가 작동하는 방식을 명확히 설명합니다.

출력에는 다음 패턴이 표시됩니다.

  • 그룹은 이전 그룹이 출력을 표시하는 동안 시작할 수 있지만 이전 그룹 출력의 표시는 중단되지 않습니다.

    #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
    
  • pendingWork 작업은 먼저 시작된 그룹 A의 경우에만 FinishOneGroupAsync 시작 시 널(Visual Basic에서 Nothing)입니다. 그룹 A는 아직 FinishOneGroupAsync에 도달했을 때 await 식을 완료하지 않았습니다. 따라서 제어가 AccessTheWebAsync로 반환되지 않았고 pendingWork으로 첫 번째 할당이 발생되지 않았습니다.

  • 다음 두 줄은 항상 출력에 함께 표시됩니다. 코드는 StartButton_Click에서 그룹 작업을 시작하고 그룹에 대한 작업을 pendingWork로 할당하는 사이에 중단되지 않습니다.

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

    그룹이 StartButton_Click을 입력한 후에는 작업은 await 식을 완료한 다음 FinishOneGroupAsync를 입력합니다. 따라서 코드의 해당 세그먼트 중 다른 작업이 컨트롤을 얻을 수 없습니다.

예제 응용 프로그램 검토 및 실행

예제 응용 프로그램을 더 잘 이해하려면 이를 다운로드하여 직접 작성하거나 응용 프로그램을 구현하지 않고 이 항목의 끝 부분에 있는 코드를 검토합니다.

참고

예제를 WPF(Windows Presentation Foundation) 데스크톱 응용 프로그램으로 실행하려면 Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows 또는 .NET Framework 4.5나 4.5.1이 컴퓨터에 설치되어 있어야 합니다.

예제를 Windows 스토어 응용 프로그램으로 실행하려면 컴퓨터에 Windows 8이 설치되어 있어야 합니다.또한 Visual Studio에서 예제를 실행하려면 Visual Studio 2012, Visual Studio 2013, Windows 8용 Visual Studio Express 2012 또는 Visual Studio Express 2013 for Windows가 설치되어 있어야 합니다.Visual Studio 2010은 .NET Framework 4.5를 대상으로 하는 프로젝트를 로드할 수 없습니다.

응용 프로그램 다운로드

  1. 압축 파일을 Async Samples: Reentrancy in .NET Desktop Apps 또는 Async Samples: Reentrancy in Windows Store Apps에서 다운로드합니다.

  2. 다운로드한 파일을 압축한 다음 Visual Studio를 시작합니다.

  3. 메뉴 모음에서 파일, 열기, 프로젝트/솔루션을 선택합니다.

  4. 압축을 푼 샘플 코드가 저장된 폴더로 이동한 후 솔루션 파일(.sln)을 엽니다.

  5. 솔루션 탐색기에서 실행하려는 프로젝트에 대한 바로 가기 메뉴를 연 후 시작 프로젝트로 설정을 선택합니다.

  6. Ctrl+F5를 선택하여 프로젝트를 빌드하고 실행합니다.

응용 프로그램 빌드

다음 단원에서는 WPF 응용 프로그램 또는 Windows 스토어 응용 프로그램으로 예제를 빌드하기 위한 코드를 제공합니다.

WPF 응용 프로그램을 빌드하려면

  1. Visual Studio를 시작합니다.

  2. 메뉴 모음에서 파일, 새로 만들기, 프로젝트를 선택합니다.

    새 프로젝트 대화 상자가 열립니다.

  3. 설치된 템플릿 창에서 Visual Basic 또는 **Visual C#**을 확장한 다음 Windows를 확장합니다.

  4. 프로젝트 형식 목록에서 WPF 응용 프로그램을 선택합니다.

  5. 프로젝트 이름을 WebsiteDownloadWPF로 지정한 후 확인 단추를 선택합니다.

    솔루션 탐색기에 새 프로젝트가 나타납니다.

  6. Visual Studio 코드 편집기에서 MainWindow.xaml 탭을 선택합니다.

    탭이 표시되지 않는 경우 솔루션 검색기에서 MainPage.xaml의 바로 가기 메뉴를 연 다음코드 보기를 선택합니다.

  7. MainWindow.xaml의 XAML 보기에서 코드를 다음 코드로 바꿉니다.

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

    텍스트 상자 및 단추가 포함된 간단한 창이 MainWindow.xaml의 디자인 뷰에 나타납니다.

  8. System.Net.Http에 대해 참조를 추가합니다.

  9. 솔루션 탐색기에서 MainWindow.xaml.vb 또는 MainWindow.xaml.cs의 바로 가기 메뉴를 열고 코드 보기를 선택합니다.

  10. MainWindow.xaml.vb 또는 MainWindow.xaml.cs에서 코드를 다음 코드로 바꿉니다.

    ' 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. 프로그램을 실행하려면 Ctrl+F5를 선택한 다음 시작 단추를 몇 번 선택합니다.

  12. 재입력을 처리하도록 시작 단추 사용 안 함, 작업 취소 및 다시 시작 또는 여러 작업을 실행하고 출력을 큐 대기에서 변경합니다.

Windows 스토어 응용 프로그램을 빌드하려면

  1. Visual Studio를 시작합니다.

  2. 메뉴 모음에서 파일, 새로 만들기, 프로젝트를 선택합니다.

    새 프로젝트 대화 상자가 열립니다.

  3. 설치됨템플릿 범주에서 Visual Basic 또는 **Visual C#**을 확장한 다음 Windows 스토어를 확장합니다.

  4. 프로젝트 형식 목록에서 **새 응용 프로그램(XAML)**을 선택합니다.

  5. 프로젝트 이름을 WebsiteDownloadWin으로 지정한 후 확인 단추를 선택합니다.

    솔루션 탐색기에 새 프로젝트가 나타납니다.

  6. 솔루션 탐색기에서 MainPage.xaml의 바로 가기 메뉴를 열고 열기를 선택합니다.

  7. MainPage.xaml의 XAML 창에서 코드를 다음 코드로 바꿉니다.

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

    텍스트 상자 및 시작 단추가 포함된 간단한 창이 MainPage.xaml의 디자인 창에 나타납니다.

  8. 솔루션 탐색기에서 MainPage.xaml.vb 또는 MainPage.xaml.cs의 바로 가기 메뉴를 열고 코드 보기를 선택합니다.

  9. MainPage.xaml.vb 또는 MainPage.xaml.cs의 코드를 다음 코드로 바꿉니다.

    ' 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. 프로그램을 실행하려면 Ctrl+F5를 선택한 다음 시작 단추를 몇 번 선택합니다.

  11. 재입력을 처리하도록 시작 단추 사용 안 함, 작업 취소 및 다시 시작 또는 여러 작업을 실행하고 출력을 큐 대기에서 변경합니다.

참고 항목

작업

연습: Async 및 Await를 사용하여 웹에 액세스(C# 및 Visual Basic)

개념

Async 및 Await를 사용한 비동기 프로그래밍(C# 및 Visual Basic)

기타 리소스

비동기 프로그래밍(Windows Store 앱)

퀵 스타트: C# 또는 Visual Basic에서 비동기 API 호출