使用Parallel.ForEach和/或async/await

3

我尝试验证我的图片URL,以确定它们是否有效。由于我有很多URL需要验证,完成此任务需要数小时时间。因此,我决定异步进行操作。我想知道按照下面的代码进行操作是否有任何巨大的差异或优势。

我的主要功能如下:

Async Function testUrl_async(ByVal myImageurl As String) As Task(Of Boolean)

   myHttpResponse = Await myHttpClient.GetAsync(myImageurl)
    If myHttpResponse.IsSuccessStatusCode Then
        mySuccess = True
    Else
        mySuccess = False
    End If

    Return mySuccess
End Function

 Function testUrl(ByVal myImageurl As String) As  Boolean

   myHttpResponse = myHttpClient.GetAsync(myImageurl)
    If myHttpResponse.IsSuccessStatusCode Then
        mySuccess = True
    Else
        mySuccess = False
    End If

    Return mySuccess
End Function

1)使用async await。

For Each myImage In myImages
    Dim result=await testUrl_async(myImageUrl).Result 
    'some code                  
Next

2) 使用并行foreach

Parallel.ForEach(myImages, 
    Sub(myImage)
        testUrl(pictureComponent.websiteShop.hqpatronen, myImageUrl) 
        'some code
    End Sub)

3) 使用并行foreach和异步/等待

Parallel.ForEach(myImages, 
    Sub(myImage)
        await testUrl_async(pictureComponent.websiteShop.hqpatronen, myImageUrl) 
    'some code
    End Sub)

第三种可能是最好的解决方案,但它不允许我在ForEach中调用Await/Async。
如果我使用第二种方法,则testurl函数具有异步http调用,但不带Await,因此会崩溃并显示异常消息:
[TaskCanceledException: A task was canceled.]
在调用myHttpClient.GetAsync的行上。我猜这会抛出此异常,因为ForEach已经结束,并且请求取消了,但是httpclient还没有完成其工作。如果这可能是最佳解决方案,我该如何处理?
或者任何其他使我的工作更快的解决方案。

1
@batmaci 就像 async/await 一样,是的。 - Panagiotis Kanavos
@PanagiotisKanavos async/await 在 .NET 4.0 中得到支持。我不认为 TPL Dataflow 支持它。 - Cory Nelson
只有在添加了Microsoft.Build和Microsoft.Bcl.Async包之后,才能实际修改构建过程以允许这些关键字。这是一项兼容性措施,而不是实际支持。 - Panagiotis Kanavos
1
@PanagiotisKanavos 关键字是 C# 的一部分,而不是框架的一部分。所有兼容性包添加的都是必要的框架位,以使关键字正常工作。 - Cory Nelson
1
@CoryNelson 并不完全准确,因为async/await并不需要任何额外的IL。即使在C#5中,编译器也会生成状态机来处理等待。这就是为什么需要Microsoft.Build——它实际上改变了项目构建的方式。兼容性包不会修改运行时本身,也不会添加任何新的IL。 - Panagiotis Kanavos
显示剩余2条评论
1个回答

11

你肯定不想使用 Parallel.ForEachParallel 用于将 CPU 密集型算法分散到多个核心上,这对你没有任何好处(在你的场景中,你的算法不是 CPU 密集型的)。

你实际需要的是并发,而非并行。可以使用异步并发来完成,使用 Task.WhenAll

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

我第一种方法使用经典的for each和sync-await之间有很大的性能(速度)差异吗? - Emil
我测试了一下,确实产生了很大的差异。谢谢你的帮助 :) - Emil
你可以帮忙看一下我其他相关的帖子吗?我并不想再提出更多问题,而是创建了一个新的帖子。http://stackoverflow.com/questions/31027885/how-can-i-control-thread-count-when-i-use-task-whenall - Emil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接