在大型 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 甚至会这样做)长文件列表可能会提供。
我正在查询数据库并检索数十万条记录。然后我阅读返回的 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 甚至会这样做)长文件列表可能会提供。