0

これは、 httpclient.GetStringAsync ブロッキングの下に投稿されたコードの更新バージョンです。

問題は、タスクがキャンセルされたにもかかわらず、キャンセルがいつ行われたかAwait Task.WhenAll(tasks)ですfinally。キャンセルを押すとタスクのキャンセルが表示され、接続が 0 に減少することもわかりますが、最終的な推測でWhenAllはまだいくつかのタスクが実行されていると考えられます。

コードは次のとおりです。

Private concurrencySemaphore As New SemaphoreSlim(10)
Private cts As CancellationTokenSource
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Try
        cts = New CancellationTokenSource
        Dim urls As List(Of String) = SetUpURLList()
        ServicePointManager.DefaultConnectionLimit = 10
        Dim tasks As List(Of Task) = New List(Of Task)()
        For Each url In urls
            cts.Token.ThrowIfCancellationRequested()
            tasks.Add(GetUrl(url, cts.Token))
        Next
        Await Task.WhenAll(tasks)
    Catch tx As TaskCanceledException
        Console.WriteLine("Task was cancelled")
    Catch ox As OperationCanceledException
        Console.WriteLine("Operation was cancelled")
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        Console.WriteLine("Done")
    End Try
End Sub
Async Function GetUrl(url As String, ByVal ct As CancellationToken) As Task
    Try
        ct.ThrowIfCancellationRequested()
        Await concurrencySemaphore.WaitAsync()
        Dim baseAddress = New Uri("http://www.amazon.com")
        Dim cookies As New CookieContainer()
        Dim handler As New HttpClientHandler With {.CookieContainer = cookies, _
                                                   .UseCookies = True}
        Dim httpClient = New HttpClient(handler) With {.BaseAddress = baseAddress}
        ct.ThrowIfCancellationRequested()
        Dim responseMessage As HttpResponseMessage = Await httpClient.GetAsync(url, ct).ConfigureAwait(False)
        Dim response As String = Await responseMessage.Content.ReadAsStringAsync()
        For Each cook As Cookie In cookies.GetCookies(baseAddress)
            Console.WriteLine(cook.Name & "=" & cook.Value)
        Next
        httpClient.Dispose()
        concurrencySemaphore.Release()
    Catch tx As TaskCanceledException
        Console.WriteLine("Task Cancelled Exception")
    Catch ox As OperationCanceledException
        Console.WriteLine("Operation Cancelled Exception")
    End Try
End Function

Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
End Sub

更新: @I3arnon の提案に基づいて質問を変更しました。

しかし、現在、以下のコードには 3 つの問題があります。

 Dim downLoader As TransformBlock(Of String, Task(Of String))
 Dim cts As CancellationTokenSource
 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    cts = New CancellationTokenSource
    Dim urls As List(Of String) = SetUpURLList()
    Dim tasks As List(Of Task) = New List(Of Task)()
    downLoader.Post(urls(0))
    downLoader.Post(urls(1))
    'For Each url In urls
    '    tasks.Add(downLoader.Post(url))
    'Next
End Sub

Private Sub TPL_Load(sender As Object, e As EventArgs) Handles Me.Load
    downLoader = New TransformBlock(Of String, Task(Of String))(
    Async Function(url) As Task(Of String)
        Console.WriteLine("Downloading URL:{0}", url)
        Dim httpClient = New HttpClient()
        Using responseMessage As HttpResponseMessage = Await httpClient.GetAsync(url).ConfigureAwait(False)
            Dim response As String = Await responseMessage.Content.ReadAsStringAsync()
            Return response
            Console.WriteLine("Downloaded, URL:{0}, length:{1}", url, response.Length)
        End Using
    End Function, New ExecutionDataflowBlockOptions With {.MaxDegreeOfParallelism = 4, .CancellationToken = cts.Token})
End Sub
  1. TransformBlock署名と本文の場所がわからないため、コードはコンパイルされません。オブジェクトが作成されていないため、内部に配置loadすると、呼び出されたときに null 例外が発生します。

  2. 複数の URL を並行して起動する方法

  3. CancellationTokenが拡張機能に渡されますが、ジョブを実行する内部メソッドには渡されないため、キャンセルを確実に行うにはどうすればよいでしょうか。

4

1 に答える 1

0

問題は、例外 (または場合によってはキャンセル) が発生したときにセマフォを解放しないことです。つまりTask.WhenAll、そのセマフォを待っているため、決して完了しないタスクを待っています。

セマフォのリリースをFinallyブロックに移動して、スキップされないようにします。

Async Function GetUrl(url As String, ByVal ct As CancellationToken) As Task
    Try
        ct.ThrowIfCancellationRequested()
        Await concurrencySemaphore.WaitAsync()
        Dim baseAddress = New Uri("http://www.amazon.com")
        Dim cookies As New CookieContainer()
        Dim handler As New HttpClientHandler With {.CookieContainer = cookies, _
                                                   .UseCookies = True}
        Dim httpClient = New HttpClient(handler) With {.BaseAddress = baseAddress}
        ct.ThrowIfCancellationRequested()
        Dim responseMessage As HttpResponseMessage = Await httpClient.GetAsync(url, ct).ConfigureAwait(False)
        Dim response As String = Await responseMessage.Content.ReadAsStringAsync()
        For Each cook As Cookie In cookies.GetCookies(baseAddress)
            Console.WriteLine(cook.Name & "=" & cook.Value)
        Next
        httpClient.Dispose()
    Catch tx As TaskCanceledException
        Console.WriteLine("Task Cancelled Exception")
    Catch ox As OperationCanceledException
        Console.WriteLine("Operation Cancelled Exception")
    Finally
        concurrencySemaphore.Release()
    End Try
End Function
于 2015-01-01T08:16:11.967 に答える