다음을 통해 공유


방법: Task.WhenAll을 사용하여 비동기 자습서 확장(Visual Basic)

메서드를 사용하여 Async 및 Await(Visual Basic)를 사용하여 웹에 액세스하는 연습에서 비동 기 솔루션의 Task.WhenAll 성능을 향상시킬 수 있습니다. 이 메서드는 작업 컬렉션으로 표시되는 여러 비동기 작업을 비동기적으로 대기합니다.

살펴보기에서 웹 사이트가 다른 속도로 다운로드되는 것을 알아챘을 수 있습니다. 때로는 웹 사이트 중 하나가 매우 느려서 나머지 다운로드가 모두 지연됩니다. 연습에서 빌드하는 비동기 솔루션을 실행하는 경우 기다리지 않으려는 경우 프로그램을 쉽게 종료할 수 있지만, 더 나은 옵션은 모든 다운로드를 동시에 시작하고 지연된 다운로드를 기다리지 않고 더 빠른 다운로드를 계속하도록 하는 것입니다.

작업 컬렉션에 Task.WhenAll 메서드를 적용합니다. 컬렉션의 모든 작업이 완료될 때까지 완료되지 않는 단일 작업을 반환하는 WhenAll를 적용합니다. 태스크는 병렬로 실행되는 것처럼 보이지만 추가 스레드는 만들어지지 않습니다. 작업은 순서에 따라 완료할 수 있습니다.

중요합니다

다음 절차는 연습: Async 및 Await를 사용하여 웹에 액세스(Visual Basic)에서 개발된 비동기 애플리케이션의 확장에 대해 설명합니다. 연습을 완료하거나 .NET 샘플 브라우저에서 샘플을 다운로드하여 애플리케이션을 개발할 수 있습니다. 예제 코드는 SerialAsyncExample 프로젝트에 있습니다.

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

Task.WhenAll을 GetURLContentsAsync 솔루션에 추가하려면

  1. ProcessURLAsync 메서드를 연습: Async 및 Await를 사용하여 웹에 액세스하기 (Visual Basic)에 따라 개발된 첫 번째 애플리케이션에 추가합니다.

    • 개발자 코드 샘플에서 코드를 다운로드한 경우 AsyncWalkthrough 프로젝트를 연 다음 MainWindow.xaml.vb 파일에 추가 ProcessURLAsync 합니다.

    • 연습을 완료하여 코드를 개발한 경우 ProcessURLAsync을(를) 메서드를 포함하는 애플리케이션에 추가합니다. 이 애플리케이션에 대한 MainWindow.xaml.vb 파일은 "연습의 코드 예제 완료" 섹션의 첫 번째 예제입니다.

    ProcessURLAsync 메서드는 원래의 워크스루에서 For Each 루프의 SumPageSizesAsync 본문 작업을 통합합니다. 이 메서드는 지정된 웹 사이트의 콘텐츠를 바이트 배열로 비동기적으로 다운로드한 다음 바이트 배열의 길이를 표시하고 반환합니다.

    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)
    
        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. 다음 코드가 표시하는 것처럼 For EachSumPageSizesAsync 루프를 주석 처리하거나 삭제합니다.

    'Dim total = 0
    'For Each url In urlList
    
    '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    '    ' The previous line abbreviates the following two assignment statements.
    
    '    ' GetURLContentsAsync returns a task. At completion, the task
    '    ' produces a byte array.
    '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. 작업 컬렉션을 만듭니다. 다음 코드는 메서드에서 실행할 때 각 웹 사이트의 콘텐츠를 다운로드하는 작업 컬렉션을 만드는 ToArray를 정의합니다. 쿼리를 평가할 때 작업이 시작됩니다.

    선언 후 메서드 SumPageSizesAsync 에 다음 코드를 추가합니다 urlList.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. 작업 컬렉션 Task.WhenAlldownloadTasks을 적용합니다. Task.WhenAll 는 작업 컬렉션의 모든 작업이 완료될 때 완료되는 단일 작업을 반환합니다.

    선행 예제에서 Await 표현식은 WhenAll 반환되는 단일 작업의 완료를 기다립니다. 식은 정수 배열로 계산됩니다. 여기서 각 정수는 다운로드한 웹 사이트의 길이입니다. 이전 단계에서 추가한 SumPageSizesAsync코드 바로 다음에 다음 코드를 추가합니다.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. 마지막으로, 메서드를 Sum 사용하여 모든 웹 사이트의 길이 합계를 계산합니다. 에 다음 줄을 추가합니다 SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Task.WhenAll을 HttpClient.GetByteArrayAsync 솔루션에 추가하려면

  1. ProcessURLAsync의 다음 버전을 Walkthrough: Async 및 Await를 사용하여 웹에 액세스(Visual Basic)에서 개발된 두 번째 애플리케이션에 추가합니다.

    • 개발자 코드 샘플에서 코드를 다운로드한 경우 AsyncWalkthrough_HttpClient 프로젝트를 연 다음 MainWindow.xaml.vb 파일에 추가 ProcessURLAsync 합니다.

    • 연습을 완료하여 코드를 개발한 경우 ProcessURLAsync를, 메서드를 사용하는 애플리케이션에 HttpClient.GetByteArrayAsync 추가합니다. 이 애플리케이션에 대한 MainWindow.xaml.vb 파일은 "연습에서 코드 예제 완료" 섹션의 두 번째 예제입니다.

    ProcessURLAsync 메서드는 원래의 워크스루에서 For Each 루프의 SumPageSizesAsync 본문 작업을 통합합니다. 이 메서드는 지정된 웹 사이트의 콘텐츠를 바이트 배열로 비동기적으로 다운로드한 다음 바이트 배열의 길이를 표시하고 반환합니다.

    이전 절차의 ProcessURLAsync 메서드와 유일한 차이점은 인스턴스HttpClient를 사용하는 것입니다client.

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)
    
        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. 다음 코드가 표시하는 것처럼 For EachSumPageSizesAsync 루프를 주석 처리하거나 삭제합니다.

    'Dim total = 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)
    
    '    ' The following two lines can replace the previous assignment statement.
    '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. 메서드에 의해 실행될 때 각 웹 사이트의 콘텐츠를 다운로드하는 태스크 컬렉션을 만드는 ToArray를 정의합니다. 쿼리를 평가할 때 작업이 시작됩니다.

    메서드 SumPageSizesAsync의 선언 후 clienturlList 다음에 다음 코드를 추가하십시오.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url, client)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. 다음으로 Task.WhenAll를 작업 컬렉션 downloadTasks에 적용합니다. Task.WhenAll 는 작업 컬렉션의 모든 작업이 완료될 때 완료되는 단일 작업을 반환합니다.

    선행 예제에서 Await 표현식은 WhenAll 반환되는 단일 작업의 완료를 기다립니다. 완료되면 식은 Await 정수 배열로 계산됩니다. 여기서 각 정수는 다운로드한 웹 사이트의 길이입니다. 이전 단계에서 추가한 SumPageSizesAsync코드 바로 다음에 다음 코드를 추가합니다.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. 마지막으로, 메서드를 Sum 사용하여 모든 웹 사이트의 길이 합계를 가져옵니다. 에 다음 줄을 추가합니다 SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Task.WhenAll 솔루션을 테스트하려면

두 솔루션 중 하나에서 F5 키를 선택하여 프로그램을 실행한 다음 시작 단추를 선택합니다. 출력은 비동기 및 대기를 사용하여 웹에 액세스하는 방법 (Visual Basic)의 워크스루에서 비동기 솔루션의 출력과 비슷하게 나와야 합니다. 그러나 웹 사이트는 매번 다른 순서로 표시됩니다.

예제 1

다음 코드에서는 이 메서드를 사용하여 GetURLContentsAsync 웹에서 콘텐츠를 다운로드하는 프로젝트의 확장을 보여 줍니다.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

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

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        'Dim total = 0
        'For Each url In urlList

        '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)

        '    ' The previous line abbreviates the following two assignment statements.

        '    ' GetURLContentsAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

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

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    ' The actions from the foreach loop are moved to this async method.
    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)

        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content.
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL.
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for
        ' the response.
        Using response As WebResponse = Await webReq.GetResponseAsync()
            ' Get the data stream that is associated with the specified URL.
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.
                ' CopyToAsync returns a Task, not a Task<T>.
                Await responseStream.CopyToAsync(content)
            End Using
        End Using

        ' Return the result as a byte array.
        Return content.ToArray()
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' 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.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

예제 2

다음 코드에서는 메서드 HttpClient.GetByteArrayAsync 를 사용하여 웹에서 콘텐츠를 다운로드하는 프로젝트의 확장을 보여 줍니다.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The
        ' default buffer size is 65,536.
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

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

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        ''<snippet7>
        'Dim total = 0
        'For Each url In urlList
        '    ' GetByteArrayAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    '<snippet31>
        '    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
        '    '</snippet31>

        '    ' The following two lines can replace the previous assignment statement.
        '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

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

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://www.msdn.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)

        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' 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.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

참고하십시오