我应该为ForEachAsync扩展方法指定什么Dop参数?

本文关键字:什么 Dop 参数 ForEachAsync 扩展 方法 我应该 | 更新日期: 2023-09-27 18:19:07

我最近发现下面的代码可以有效地运行大量的I/O绑定任务:
实现一个简单的ForEachAsync,第2部分

在我的印象中,下列情况是正确的:

  • 这比使用Parallel.ForEach要好得多,因为工作不受CPU限制。
  • ForEachAsync将帮助将尽可能多的IO任务排队(不必将它们放在单独的线程上)。
  • TPL将"知道"这些是基于IO的任务,而不是启动更多的线程,而是使用回调/任务完成源向主线程发送信号,从而节省线程上下文切换的开销。

我的问题是,由于Parallel.ForEach本质上有自己的MaxDegreeOfParallelism定义,我如何知道在IEnumerable扩展的示例代码中定义dop参数到这里?

。如果我有1000个项目要处理,并且需要为每个项目执行基于IO的SQL-Server db调用,我会指定1000作为dop吗?对于Parallel.ForEach,它被用作限制器,以防止过多的线程旋转,这可能会损害性能。但在这里,它似乎用于划分最小异步任务的数量。我认为应该至少没有这样的最大值(最小值是要处理的总项目),因为我想将尽可能多的基于IO的调用排队到数据库。

我怎么知道要看到DOP参数呢?

public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body) 
{ 
    return Task.WhenAll( 
        from partition in Partitioner.Create(source).GetPartitions(dop) 
        select Task.Run(async delegate { 
            using (partition) 
                while (partition.MoveNext()) 
                    await body(partition.Current); 
        })); 
}

我应该为ForEachAsync扩展方法指定什么Dop参数?

平行。ForEach本质上有自己的MaxDegreeOfParallelism

好的,Parallel.ForEach内置的启发式很容易随着时间的推移产生大量的任务(如果你的工作项有10ms的延迟,你会在一个小时左右得到数百个任务——我测量过)。真的很糟糕的设计缺陷,不要试图模仿这个。

当并行运行IO时,没有什么可以替代经验确定正确的值。这就是为什么TPL在这方面如此糟糕。例如,执行顺序IO的磁盘的DOP为1。一个随机的SSD基本上是无限的(100?)。

远程web服务无法让您知道正确的DOP。你不仅需要测试,你还需要请求所有者允许你向服务发送可能会使其过载的请求。

我将指定1000作为dop吗?

那么你就根本不需要这个设施了。只需要刷出所有任务,然后等待所有任务完成。但是1000可能是错误的DOP,因为它压倒了DB而没有任何好处。

这里似乎是用来划分异步任务的最小数量

Parallel.For的另一个可怕的特性。在低CPU机器上,它可能会生成小的任务。可怕的API。不要与IO一起使用。(我使用AsParallel,它允许您设置精确 DOP,而不是最大DOP。)

因为我想让尽可能多的基于IO的数据库调用排队

为什么?不是个好计划。


顺便说一句,你贴在这里的方法很好,我也用这个。我希望它在框架中。这种确切的方法是每周大约1050个问题("我如何并行异步处理100000个项目?")的答案。