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会导致空异常。

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