다음을 통해 공유


비동기 앱에서 재진입 처리(Visual Basic)

앱에 비동기 코드를 포함하는 경우 비동기 작업을 완료하기 전에 다시 입력하는 것을 의미하는 재진입을 고려하고 방지해야 합니다. 재진입 가능성을 식별하고 처리하지 않으면 예기치 않은 결과가 발생할 수 있습니다.

비고

예제를 실행하려면 Visual Studio 2012 이상과 .NET Framework 4.5 이상이 컴퓨터에 설치되어 있어야 합니다.

비고

TLS(전송 계층 보안) 버전 1.2는 이제 앱 개발에 사용할 최소 버전입니다. 앱이 4.7 이전의 .NET Framework 버전을 대상으로 하는 경우 .NET Framework를 사용하는 TLS(전송 계층 보안) 모범 사례에 대한 다음 문서를 참조하세요.

재진입 가능성 인식

이 항목의 예제에서 사용자는 시작 단추를 선택하여 일련의 웹 사이트를 다운로드하고 다운로드된 총 바이트 수를 계산하는 비동기 앱을 시작합니다. 이 예제의 동기 버전은 사용자가 단추를 선택한 횟수에 관계없이 동일한 방식으로 응답합니다. 첫 번째 이후 앱 실행이 완료될 때까지 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 단추를 다시 활성화할 수 있습니다.

다음 코드에서는 별표로 표시된 이러한 변경 내용을 보여 줍니다. 이 항목의 끝에 코드에 변경 내용을 추가하거나 비동기 샘플: .NET Desktop 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

변경으로 인해 웹 사이트를 다운로드하는 동안 AccessTheWebAsync 단추가 응답하지 않으므로 프로세스를 다시 입력할 수 없습니다.

작업 취소 및 다시 시작

시작 단추를 사용하지 않도록 설정하는 대신 단추를 활성 상태로 유지할 수 있지만 사용자가 해당 단추를 다시 선택하는 경우 이미 실행 중인 작업을 취소하고 가장 최근에 시작된 작업을 계속할 수 있습니다.

취소에 대한 자세한 내용은 Fine-Tuning 비동기 애플리케이션(Visual Basic)을 참조하세요.

이 시나리오를 설정하려면 예제 앱 검토 및 실행에 제공되는 기본 코드를 다음과 같이 변경합니다. .NET 데스크톱 앱에서의 비동기 샘플: 재진입에서 완성된 앱을 다운로드할 수 있습니다. 이 프로젝트의 이름은 CancelAndRestart입니다.

  1. 모든 메서드에서 사용할 수 있는 CancellationTokenSource 범위의 변수 cts를 선언합니다.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. 에서 StartButton_Click작업이 이미 진행 중인지 여부를 확인합니다. 값이 cts이면 Nothing 작업이 활성화되어 있지 않습니다. 값이 없 Nothing으면 이미 실행 중인 작업이 취소됩니다.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. 현재 프로세스를 나타내는 다른 값으로 설정합니다 cts .

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. 끝부StartButton_Click분에 현재 프로세스가 완료되므로 다시 cts값을 Nothing .로 설정합니다.

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

다음 코드는 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

AccessTheWebAsync에서 다음을 변경하세요.

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

  • 웹사이트를 다운로드하려면 GetAsync 메서드를 사용하십시오. 이는 GetAsyncCancellationToken 인수를 허용하기 때문입니다.

  • 다운로드한 각 웹 사이트에 대한 결과를 표시하기 위해 호출 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

이 앱을 실행하는 동안 시작 단추를 여러 번 선택하면 다음 출력과 유사한 결과가 생성됩니다.

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 줄의 주석 처리를 제거하여 텍스트 상자를 지웁니다.

여러 작업 실행 및 출력 큐 대기

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

운영은 디스플레이 프로세스의 게이트키퍼 역할을 하는 전역 TaskpendingWork를 공유합니다.

앱 빌드의 코드에 변경 내용을 붙여넣어 이 예제를 실행하거나, 앱을 다운로드하는 지침에 따라 샘플을 다운로드한 다음 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)

변수TaskpendingWork 표시 프로세스를 감독하고 그룹이 다른 그룹의 표시 작업을 방해하지 않도록 합니다. 문자 변수는 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

AccessTheWebAsync 메서드

이 예제에서는 두 가지 메서드로 분할됩니다 AccessTheWebAsync . 첫 번째 메서드는 AccessTheWebAsync그룹에 대한 모든 다운로드 작업을 시작하고 표시 프로세스를 제어하도록 설정합니다 pendingWork . 이 메서드는 LINQ 쿼리(언어 통합 쿼리) ToArray 를 사용하여 모든 다운로드 작업을 동시에 시작합니다.

AccessTheWebAsync 그런 다음 각 다운로드 완료를 기다리고 길이를 표시하기 위해 호출 FinishOneGroupAsync 합니다.

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

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

FinishOneGroupAsync 메서드

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

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

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

앱 빌드의 코드에 변경 내용을 붙여넣어 이 예제를 실행하거나, 앱을 다운로드하는 지침에 따라 샘플을 다운로드한 다음 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
    
  • 작업은 A 그룹,pendingWorkNothing 가장 먼저 시작된 그룹에 대해서만 FinishOneGroupAsync 시작됩니다. 그룹 A는 FinishOneGroupAsync에 도달했을 때 아직 await 식을 완료하지 않았습니다. 따라서 AccessTheWebAsync에 대한 컨트롤이 반환되지 않았고, pendingWork에 대한 첫 번째 할당이 발생하지 않았습니다.

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

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

    그룹이 StartButton_Click에 들어가면, 작업이 FinishOneGroupAsync에 이를 때까지 await 식을 완료하지 않습니다. 따라서 해당 코드 세그먼트 동안 다른 작업을 제어할 수 없습니다.

예제 앱 검토 및 실행

예제 앱을 더 잘 이해하려면 앱을 구현하지 않고도 이 항목의 끝부분에 있는 코드를 다운로드하거나 직접 빌드하거나 검토할 수 있습니다.

비고

예제를 WPF(Windows Presentation Foundation) 데스크톱 앱으로 실행하려면 Visual Studio 2012 이상과 .NET Framework 4.5 이상이 컴퓨터에 설치되어 있어야 합니다.

앱 다운로드

  1. Async Samples: .NET Desktop Apps의 재진입에서 압축된 파일을 다운로드합니다.

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

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

  4. 압축 해제된 샘플 코드를 포함하는 폴더로 이동한 다음 솔루션(.sln) 파일을 엽니다.

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

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

앱 빌드

다음 섹션에서는 예제를 WPF 앱으로 빌드하는 코드를 제공합니다.

WPF 앱을 빌드하려면
  1. Visual Studio를 시작합니다.

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

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

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

  4. 프로젝트 형식 목록에서 WPF 애플리케이션을 선택합니다.

  5. 프로젝트 WebsiteDownloadWPF이름을 지정하고 .NET Framework 버전 4.6 이상을 선택한 다음 확인 단추를 클릭합니다.

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

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

    탭이 표시되지 않으면 솔루션 탐색기에서 MainWindow.xaml의 바로 가기 메뉴를 열고 코드 보기를 선택합니다.

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

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://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 대한 바로 가기 메뉴를 연 다음 코드 보기를 선택합니다.

  10. MainWindow.xaml.vb 코드를 다음 코드로 바꿉다.

    ' 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)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' 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/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/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("https://", "")
            ' 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
    
  11. Ctrl+F5 키를 선택하여 프로그램을 실행한 다음 시작 단추를 여러 번 선택합니다.

  12. 시작 버튼 사용 안 함, 작업 취소 및 다시 시작 또는 여러 작업 실행 및 출력 큐 대기를 통해 재진입을 처리하도록 변경합니다.

참고하십시오