当我使用“Task.WhenAll”时,如何控制线程数

时间:2015-06-24 13:25:37

标签: vb.net multithreading async-await semaphore

我通过异步发出http get请求来验证图片网址。所有工作都可以正常使用下面的代码,但是当我有这么多图片时,我们的防火墙将阻止我的互联网访问,因为有很多线程同时请求。因此,我一直在寻找一种解决方案,如何限制并发运行线程的数量。我结束了这个thread告诉我使用SemaphoreSlim,但我不知道怎么能得到这个想法以及如何实现它?

  • 是SemaphoreSlim等待还是等待Asnyc(无论如何有什么不同?)在添加任务时应该在foreach中?我可以像在代码中一样使用linq创建任务列表吗?
    • 为什么有使用任务。运行?
    • 执行哪一行之后线程是否启动?在task.run或task.whenall之后?

如果这不是最好的方法,请建议一个更好的方法。我不确定使用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

3 个答案:

答案 0 :(得分:4)

  

我们的防火墙将阻止我的互联网访问,因为有很多线程同时请求。因此,我一直在寻找一种解决方案,如何限制并发运行线程的数量。

非常确定防火墙限制了连接数,因此您希望限制连接数(不是线程

  

是SemaphoreSlim等待还是等待Asnyc(无论如何有什么区别?)

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

  在添加任务时,

应该在foreach中吗?我可以像在代码中一样使用linq创建任务列表吗?

您可以这样做:显式构建列表,或使用LINQ。

  

为什么有使用task.Run?

那个答案中的错误。这里肯定不需要Task.Run

  

执行哪一行之后线程是否启动?在task.run或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

答案 1 :(得分:0)

您可以像这样使用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.
}

答案 2 :(得分:-1)

我假设您不在WPF或Windows Form Thread中。如果您在await之后只有一个线程正在工作。

由于我们假设您不在这些线程中,ThreadPool用于执行await之后的延续。您可以使用ThreadPool.SetMaxThreads更改池使用的线程数量,但我建议您不要这样做,并将其留给.NET来做正确的事情。这通常是最好的方式。