다음을 통해 공유


작업이 완료된 후 남은 비동기 작업 취소(Visual Basic)

메서드를 Task.WhenAny 함께 CancellationToken사용하면 하나의 작업이 완료되면 나머지 작업을 모두 취소할 수 있습니다. 메서드는 WhenAny 작업 컬렉션인 인수를 사용합니다. 메서드는 모든 작업을 시작하고 단일 작업을 반환합니다. 컬렉션의 모든 작업이 완료되면 단일 작업이 완료됩니다.

이 예제는 WhenAny와 함께 취소 토큰을 사용하여 작업 컬렉션 중 가장 먼저 완료되는 작업을 선택하고, 나머지 작업은 취소하는 방법을 보여줍니다. 각 작업은 웹 사이트의 콘텐츠를 다운로드합니다. 이 예제에서는 완료할 첫 번째 다운로드의 콘텐츠 길이를 표시하고 다른 다운로드를 취소합니다.

비고

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

예제 다운로드

비동기 샘플: 애플리케이션 미세 조정에서 전체 WPF(Windows Presentation Foundation) 프로젝트를 다운로드한 다음 다음 단계를 수행할 수 있습니다.

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

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

  3. 프로젝트 열기 대화 상자에서 압축을 푼 샘플 코드가 들어 있는 폴더를 연 다음 AsyncFineTuningVB에 대한 솔루션(.sln) 파일을 엽니다.

  4. 솔루션 탐색기에서 CancelAfterOneTask 프로젝트의 바로 가기 메뉴를 열고 시작 프로젝트로 설정을 선택합니다.

  5. F5 키를 선택하여 프로젝트를 실행합니다.

    프로젝트를 디버깅하지 않고 실행하려면 Ctrl+F5 키를 선택합니다.

  6. 프로그램을 여러 번 실행하여 다른 다운로드가 먼저 완료되는지 확인합니다.

프로젝트를 다운로드하지 않으려면 이 항목의 끝에 MainWindow.xaml.vb 파일을 검토할 수 있습니다.

예제를 구축하기

이 항목의 예제는 비동기 작업 취소 또는 작업 목록에서 개발된 프로젝트에 추가되어 작업 목록을 취소합니다. 이 예제에서는 취소 단추가 명시적으로 사용되지는 않지만 동일한 UI를 사용합니다.

예제를 직접 빌드하려면 단계별로 "예제 다운로드" 섹션의 지침에 따라 시작 프로젝트CancelAListOfTasks를 선택합니다. 이 항목의 변경 내용을 해당 프로젝트에 추가합니다.

CancelAListOfTasks 프로젝트의 MainWindow.xaml.vb 파일에서 루프 내 각 웹 사이트의 처리 단계를 다음 비동기 메서드로 이동하여 작업을 시작합니다.

' ***Bundle the processing steps for a website into one async method.
Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

    ' GetAsync returns a Task(Of HttpResponseMessage).
    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

    Return urlContents.Length
End Function

이 예제에서는 AccessTheWebAsync 쿼리와 ToArray 메서드 및 WhenAny 메서드를 사용하여 작업 배열을 생성하고 시작합니다. WhenAny을 배열에 적용하면 대기 시 배열의 태스크들 중 가장 먼저 완료되는 태스크로 평가되는 단일 작업이 반환됩니다.

AccessTheWebAsync에서 다음을 변경합니다. 별표는 코드 파일의 변경 내용을 표시합니다.

  1. 루프를 주석 처리하거나 삭제합니다.

  2. 실행할 때 제네릭 태스크 컬렉션을 생성하는 쿼리를 만듭니다. 각 호출은 ProcessURLAsync를 호출하며, Task<TResult>TResult의 반환값을 나타내며, TResult는 정수입니다.

    ' ***Create a query that, when executed, returns a collection of tasks.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url, client, ct)
    
  3. 쿼리를 실행하고 작업을 시작하도록 호출 ToArray 합니다. 다음 단계에서 WhenAny 메서드를 적용하면 ToArray을 사용하지 않고 쿼리를 실행하고 작업을 시작합니다. 하지만 다른 메서드에서는 이렇게 실행되지 않을 수 있습니다. 가장 안전한 방법은 쿼리를 명시적으로 강제로 실행하는 것입니다.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. 작업 모음에 대해 콜 기능을 사용하여 WhenAny를 호출합니다. WhenAnyTask(Of Task(Of Integer)) 또는 Task<Task<int>>를 반환합니다. 즉, WhenAny는 대기할 때 단일 Task(Of Integer) 또는 Task<int>로 평가되는 작업을 반환합니다. 이 단일 작업은 컬렉션에서 완료할 첫 번째 작업입니다. 먼저 완료된 작업이 에 할당됩니다 finishedTask. finishedTask의 형식은 정수인 Task<TResult>의 위치이며, 이는 TResult의 반환 형식이 ProcessURLAsync이기 때문입니다.

    ' ***Call WhenAny and then await the result. The task that finishes
    ' first is assigned to finishedTask.
    Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
    
  5. 이 예제에서는 먼저 완료되는 작업에만 관심이 있습니다. 따라서 나머지 작업을 취소하는 데 사용합니다 CancellationTokenSource.Cancel .

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. 마지막으로 다운로드한 콘텐츠의 길이를 검색하기 위해 기다립니다 finishedTask .

    Dim length = Await finishedTask
    resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    

프로그램을 여러 번 실행하여 다른 다운로드가 먼저 완료되는지 확인합니다.

완성된 예시

다음 코드는 예제에 대한 전체 MainWindow.xaml.vb 또는 MainWindow.xaml.cs 파일입니다. 별표는 이 예제에 추가된 요소를 표시합니다.

System.Net.Http에 대한 참조를 추가해야 함을 알아두세요.

비동기 샘플: 애플리케이션 미세 조정에서 프로젝트를 다운로드할 수 있습니다.

' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http

' Add the following Imports directive for System.Threading.
Imports System.Threading

Class MainWindow

    ' Declare a System.Threading.CancellationTokenSource.
    Dim cts As CancellationTokenSource

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

        ' Instantiate the CancellationTokenSource.
        cts = New CancellationTokenSource()

        resultsTextBox.Clear()

        Try
            Await AccessTheWebAsync(cts.Token)
            resultsTextBox.Text &= vbCrLf & "Download complete."

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

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Download failed." & vbCrLf
        End Try

        ' Set the CancellationTokenSource to Nothing when the download is complete.
        cts = Nothing
    End Sub

    ' You can still include a Cancel button if you want to.
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)

        If cts IsNot Nothing Then
            cts.Cancel()
        End If
    End Sub

    ' Provide a parameter for the CancellationToken.
    ' Change the return type to Task because the method has no return statement.
    Async Function AccessTheWebAsync(ct As CancellationToken) As Task

        Dim client As HttpClient = New HttpClient()

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

        '' Comment out or delete the loop.
        ''For Each url In urlList
        ''    ' GetAsync returns a Task(Of HttpResponseMessage).
        ''    ' Argument ct carries the message if the Cancel button is chosen.
        ''    ' Note that the Cancel button can cancel all remaining downloads.
        ''    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        ''    resultsTextBox.Text &=
        ''        vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
        ''Next

        ' ***Create a query that, when executed, returns a collection of tasks.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client, ct)

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

        ' ***Call WhenAny and then await the result. The task that finishes
        ' first is assigned to finishedTask.
        Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

        ' ***Cancel the rest of the downloads. You just want the first one.
        cts.Cancel()

        ' ***Await the first completed task and display the results
        ' Run the program several times to demonstrate that different
        ' websites can finish first.
        Dim length = Await finishedTask
        resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    End Function

    ' ***Bundle the processing steps for a website into one async method.
    Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

        ' GetAsync returns a Task(Of HttpResponseMessage).
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        Return urlContents.Length
    End Function

    ' Add a method that creates a list of web addresses.
    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "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

End Class

' Sample output:

' Length of the downloaded website:  158856

' Download complete.

참고하십시오