在大型 while 循环的每次迭代中创建任务是否存在性能 and/or 安全问题?

Is there a performance and/or safety issue with creating a task per iteration of a large while loop?

我正在查询数据库并检索数十万条记录。然后我阅读返回的 SqlReader 并为每条记录创建一个新任务。新任务然后执行一些长 运行 操作。

我的代码看起来有点像这样:

    void ProcessRecords(SqlDataReader reader)
    {
        if (!reader.HasRows)
        {
            return;
        }
        using (reader)
        {
            while (reader.Read())
            {
                var filePath = BuildFilePath(reader);
                var imageId = (int)reader["PhotoID"];
                Task.Run(() => { ProcessRecord(imageId, filePath); })
                    .ContinueWith((task) => { Progress.Report("Processing " + Path.GetFileName(filePath)); });
            }
        }
    }

高级开发人员建议我使用信号量来限制任务使用的线程数。这是解决这个问题的正确方法吗?

问题总是,

对于并行完成的单元工作,并行需要多少开销?

这取决于管理并行性的开销以及完成的工作量。

一般来说,廉价并行需要数十到数百条指令来分叉一个并行工作单元。这意味着要做的工作必须是数千条指令,以允许并行开销由正在完成的实际工作支配。

笨拙地完成(例如,"create a thread")"parallelism" 成本要大得多,因为创建线程并不便宜。大多数计算都不够昂贵,无法证明以这种方式完成的分叉。

OP 的示例可能是真正有意义的罕见情况:对磁盘执行一些事务,如果磁头必须移动,这需要数十毫秒。

一般来说,对磁盘驱动器进行并行 I/O 是行不通的;它只有一个磁头,因此对磁盘的并行操作序列化并且不重叠。

如果"ProcessRecord"时间支配磁盘时间,此代码可能有效。 (当然,OP已经测量了他的程序以查看其效果如何?)。在这种情况下,将活动线程的数量限制为 CPU 数量的一些倍数可能会实现尽可能多的并行性,而无需消耗千兆字节的内存来跟踪数万个线程(如果你的 OS 甚至会这样做)长文件列表可能会提供。