如何防止创建的线程数超过可用的硬件线程数?

How to prevent to create more threads than the available number of hardware threads?

提问人:Jackdaw 提问时间:10/27/2023 更新时间:10/27/2023 访问量:111

问:

我有以下方法,用于通过为每个文件创建不同的任务来初始化和启动日志文件处理。在任务管理器中,我可以看到这个函数正在以这种方式创建大约一百个线程。

public async Task LogProcessing(DescriptorList files, CancellationToken ct)
{
    var tasks = new List<Task>();
    foreach (var file in files)
        tasks.Add(new Task(() =>
        {
            ParsingFile(file, ct);
        }));

    foreach (var task in tasks) { task.Start(); }
   
    await Task.WhenAll(tasks);       
}

如何防止创建的线程数超过可用的硬件线程数?谢谢。

C# .NET 多线程任务 并行库

评论

0赞 D A 10/27/2023
你为什么不使用线程池?ThreadPool.QueueUserWorkItem 此外,从性能角度来看,由于重用的线程更好。(dotnettutorials.net/lesson/thread-pooling)
0赞 Jackdaw 10/27/2023
@DA:以这种方式创建的任务不会在 ThreadPool 上运行吗?
2赞 JonasH 10/27/2023
@Da 从您自己的链接“任务并行库和 PLINQ 的默认计划程序使用 .NET 线程池”
1赞 JonasH 10/27/2023
为什么不直接使用 Parallel.For/foreach-loop?这应该尝试对工作进行分区以限制使用的线程数,并允许对线程数进行显式限制。
2赞 JonasH 10/27/2023
另请注意,在尝试并行处理涉及从文件读取的工作时,需要小心。旋转磁盘讨厌随机 IO,甚至 SSD 也更喜欢顺序读取。

答:

2赞 Matthew Watson 10/27/2023 #1

如果您使用的是 .NET 6.0 或更高版本,则可以使用 Parallel.ForEachAsync() 来执行此操作:

await Parallel.ForEachAsync(
    files,
    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
    async (file, cancellation) =>
    {
        await Task.Run(() => ParsingFile(file, cancellation), cancellation);
    });

请注意,使用 to 将并发线程数限制为(逻辑)处理器计数。这通常是默认设置,因此您可能根本不需要设置它,但为了清楚起见,有些人可能希望这样做。MaxDegreeOfParallelism = Environment.ProcessorCount

根据文档“通常,您不需要修改此设置”,但在使用默认值之前,您应该查看 Theodor Zoulias 的这个答案。(我自己一般不使用默认值)。

我建议阅读有关 MaxDegreeOfParallelism 的文档,并自行决定是否指定它。

根据 TheodorZoulias 在下面的评论,您可以更简单地将其写成:

await Parallel.ForEachAsync(
    files,
    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = cancelSource.Token },
    (file, cancellation) =>
    {
        ParsingFile(file, cancellation);
        return ValueTask.CompletedTask;
    });

但是,如果是本身,您当然会使用第一个版本。ParsingFile()async

评论

1赞 Theodor Zoulias 10/27/2023
实际上,对于 API 以及 .NET 8 Parallel.ForAsync API 中的预期,这是默认值。请参阅文档Parallel.ForEachAsyncMaxDegreeOfParallelism = Environment.ProcessorCount
1赞 Theodor Zoulias 10/27/2023
也是多余的。该方法的默认值是 anyway。您只需调用 ,然后返回 (no )。await Task.Run(() =>TaskSchedulerParallel.ForEachAsyncThreadPoolParsingFileValueTask.CompletedTaskasync
1赞 Matthew Watson 10/27/2023
@TheodorZoulias我会把它添加到答案中。
1赞 Theodor Zoulias 10/27/2023
@Jackdaw顺便说一句,虽然这是默认值,但显式配置它比依赖默认值要好。我不建议从 Matthew 的回答中删除此配置。MaxDegreeOfParallelism = Environment.ProcessorCount
2赞 Ben Voigt 10/27/2023
@TheodorZoulias:我考虑的不是人们阅读你的帖子,而是更多地考虑“如果这是一个好主意,它会出现在一些财富 500 强公司的编码标准中,然后你会让人们仅仅因为它在编码标准中而不考虑它”无论编写编码标准的人是说你的帖子还是独立思考并得出相同的结论。