使用 "Task.WhenAll" 时如何控制线程数

How can I control thread count when I use "Task.WhenAll"

我正在通过异步发出 http get 请求来验证图像 url。下面的代码一切正常,但是当我有这么多图像时,我们的防火墙将阻止我的互联网访问,因为有太多线程同时请求。因此,我一直在寻找一种解决方案,如何限制并发 运行 线程的数量。我最终得到了这个 thread telling me to use SemaphoreSlim 但我不知何故无法理解这个想法以及如何实现它?

如果这不是最佳方法,请提出更好的方法。我不确定将 MaxDegreeOfParallelism 与 parallel.foreach 一起使用是否也有意义?

  Dim tasks = myImages.Select(Function(x) testUrl_async(x))
  Dim results = Await Task.WhenAll(tasks)

Async Function testUrl_async(ByVal myImage  As image) As Task(Of image)
   Dim myImageurl as string=myImage.imageurl
   myHttpResponse = Await myHttpClient.GetAsync(myImageurl)
    If myHttpResponse.IsSuccessStatusCode Then
        Return myImage
    Else
        Return Nothing
    End If
End Function

我假设您不在 WPF 或 Windows 表单线程中。如果您在 await.

之后将只有一个线程在工作

正如我们假设您不在这些线程中一样,ThreadPool is used to execute the continuations after the await. You can change the amount of Threads used by the pool with ThreadPool.SetMaxThreads 但我建议您不要那样做,让 .NET 来做正确的事情。这通常是最好的方法。

您可以像这样使用 TaskSemaphoreSlim

var MaxAllowedConcurrentRequestsCount = 20;
var guard = new SemaphoreSlim(0, MaxAllowedConcurrentRequestsCount);
foreach(var imageUrl in imageUrls)
{
    guard.Wait()
    var image = await Task.Factory.StartNew((imageUrl) => return myHttpClient.Get(imageUrl));
    guard.Release();
    // You can do whaterver you like with image. For example add it to you concurrent list.
}

our firewall will block my internet access because of so many threads concurrently requesting. Therefore I was looking for a solution how to restrict the count of concurrently running threads.

很确定防火墙正在限制您的 连接数 ,因此您想限制您的 连接数 (不是 线程).

is that SemaphoreSlim wait or waitAsnyc (what is the difference anyway?)

Wait 是同步等待 - 它会阻塞调用线程。 WaitAsync 是一个异步等待 - 它释放调用线程并在信号量可用时恢复执行当前方法。

should be inside a foreach while adding tasks? Can I just create the task list with linq as I do in my code?

您可以采用任何一种方式:显式构建列表,或使用 LINQ。

why is there used task.Run?

该答案有误。 Task.Run 在这里当然不需要或不需要。

after which line is executed does the thread start? after task.run or task.whenall?

当您调用 Task.Run 时,该委托立即排入线程池。但正如我上面所说,你不想使用 Task.Run (它也不应该在原始答案中使用)。


所以,像这样的东西就足够了:

Private _mutex As New SemaphoreSlim(20)
Async Function testUrl_async(myImage As image) As Task(Of image)
    Await _mutex.WaitAsync()
    Try
        Dim myImageurl = myImage.imageurl
        Dim myHttpResponse = Await myHttpClient.GetAsync(myImageurl)
        Return If(myHttpResponse.IsSuccessStatusCode, myImage, Nothing)
    Finally
        _mutex.Release()
    End Try
End Function