如何限制Parallel.ForEach的并发数?

374

我有一个Parallel.ForEach()异步循环,用于下载一些网页。由于我的带宽有限,所以我每次只能下载x个页面,但是Parallel.ForEach会执行整个所需的网页列表。

在运行Parallel.ForEach时,是否有限制线程数或任何其他限制器的方法?

演示代码:

Parallel.ForEach(listOfWebpages, webpage => {
  Download(webpage);
});

真正的任务与网页无关,因此创意的网络爬虫解决方案是没有帮助的。


@jKlaus,你在说什么会出问题吗? - Shiv
@Shiv,请执行几次.. https://dotnetfiddle.net/maKiI5 - jKlaus
1
@jKlaus,您正在修改一个非线程安全的元素(整数)。我预计在这种情况下它不会起作用。另一方面,OP没有修改任何需要线程安全的内容。 - Shiv
@Shiv,你确定吗?我还没有看到Download()的源代码。 - jKlaus
2
@jKlaus 这里有一个使用 Parallel.ForEach 的示例,可以正确设置计数 > https://dotnetfiddle.net/moqP2C。MSDN 链接:https://msdn.microsoft.com/zh-cn/library/dd997393(v=vs.110).aspx - jhamm
显示剩余5条评论
5个回答

694

83
这可能不适用于此特定情况,但我认为在任何人遇到这个问题并发现它有用时可以提供参考。这里我使用了75%(四舍五入)的处理器数量。var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) }; - jKlaus
7
为了避免其他人不得不在文档中查找,传递一个值为“-1”与根本不指定它是相同的:“如果[该值]为-1,则没有限制同时运行的操作数量”。 - stuartd
1
从文档中我并不清楚 - 将MaxDegreeOfParallelism设置为4(例如)是否意味着将有4个线程每个运行1/4的循环迭代(分派4个线程的一轮),还是每个线程仍然执行一个循环迭代,我们只是限制了同时运行的数量? - Hashman
17
要清楚,核心和线程不是同一回事。根据CPU的不同,每个核心的线程数也会有所不同,通常是每个核心2个线程。例如,如果您有一个4核CPU,每个核心有2个线程,则最多有8个线程。为了调整@jKlaus的评论“var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 2.0)) };”,请参考"线程 vs 核心" - https://askubuntu.com/questions/668538/cores-vs-threads-how-many-threads-should-i-run-on-this-machine - Agrejus
在给你的回答点赞之后,我意识到它是纯粹的邪恶:666 票! - Serge Wautier
@Agrejus 我认为Environment.ProcessorCount是机器上逻辑处理器的数量,而不是核心数;正如你所描述的,拥有4个核心和通常的2个线程,最多会有8个线程/逻辑处理器。在这样的设备上,Environment.ProcessCount将会是8。在jKlaus的例子中,他准确地获取了最大线程数的75%(6个中的8个),而在你的例子中,你最终得到了最大线程数的150%(12个中的8个)。 - undefined

62

您可以使用ParallelOptions并将MaxDegreeOfParallelism设置为限制并发线程的数量:

Parallel.ForEach(
    listOfwebpages, 
    new ParallelOptions{MaxDegreeOfParallelism=2}, 
    webpage => {Download(webpage);});     

25

使用另一种重载的 Parallel.Foreach,该重载接受一个 ParallelOptions 实例,并设置 MaxDegreeOfParallelism 以限制并行执行的实例数量。


17

对于VB.net用户(语法很奇怪,难以找到)...

Parallel.ForEach(listOfWebpages, New ParallelOptions() With {.MaxDegreeOfParallelism = 8}, Sub(webpage)
......end sub)  

5
我认为更具活力和现实性的方法是通过处理器数量来限制它,这样在每个系统上它才能正常运行。
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
Parallel.ForEach(myList, options, iter => { });

也许你可以通过乘以或除以Environment.ProcessorCount来增加或减少对CPU的压力。

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