使用 System.Threading.Tasks.Parallel 在线程池中创建新线程

本文关键字:线程 新线程 创建 Tasks Threading Parallel 使用 System | 更新日期: 2023-09-27 18:36:04

也许我理解得不对...所有并行类问题:(

但从我现在阅读的内容来看,我了解到,当我使用并行时,我实际上调动了线程池中存在的所有线程来完成某些任务/任务。

例如

  var arrayStrings = new string[1000];
  Parallel.ForEach<string>(arrayStrings, someString =>
  {
       DoSomething(someString);
  });

因此,在这种情况下,Parallel.ForEach 正在为"DoSomething"任务/任务调动线程池中存在的所有线程。

但是调用 Parallel.ForEach 是否会创建任何新线程?

很明显,不会有 1000 个新线程。但是让我们假设有 1000 个新线程,在某些情况下,threadPool 释放了它持有的所有线程,在这种情况下......Parallel.ForEach 会创建任何新线程吗?

使用 System.Threading.Tasks.Parallel 在线程池中创建新线程

简短回答:Parallel.ForEach()不会"动员所有线程"。任何在ThreadPool上安排某些工作的操作(Parallel.ForEach()这样做)都可能导致在池中创建新线程。

长答案:要正确理解这一点,您需要知道三个抽象级别的工作原理:Parallel.ForEach()TaskSchedulerThreadPool

  1. Parallel.ForEach()(和Parallel.For())将他们的工作安排在TaskScheduler上。如果未显式指定计划程序,则将使用当前计划程序。

    Parallel.ForEach()将工作分成几个Task。每个Task将处理输入序列的一部分,完成后,如果可用,它将请求另一部分,依此类推。

    Parallel.ForEach()将创建多少个Task?有多少TaskScheduler会让它运行。这样做的方式是,每个Task在开始执行时首先将自己的副本排队(除非这样做会违反MaxDegreeOfParallelism,如果你设置它)。这样,实际的并发级别就达到了TaskScheduler

    此外,如果TaskScheduler支持,则第一个Task将在当前线程上实际执行(这是使用 RunSynchronously() 完成的)。

  2. 默认TaskScheduler只是将每个Task排队到ThreadPool队列。(实际上,如果您从另一个Task开始Task,它会更复杂,但这在这里无关紧要。其他TaskScheduler可以做完全不同的事情,其中一些(如TaskScheduler.FromCurrentSynchronizationContext())完全不适合与Parallel.ForEach()一起使用。

  3. ThreadPool使用相当复杂的算法来准确决定在任何给定时间应该运行多少线程。但这里最重要的是,计划新工作项可能会导致创建新线程(尽管不一定立即创建)。而且因为有了Parallel.ForEach(),总有一些项目排队等待执行,所以完全由ThreadPool的内部算法来决定线程数。

放在一起,几乎不可能决定一个Parallel.ForEach()将使用多少线程,因为它取决于许多变量。两种极端都是可能的:循环将在当前线程上完全同步运行,并且每个项目都将在其自己新创建的线程上运行。

但一般来说,应该接近最佳效率,您可能不必担心所有这些细节。

Parallel.Foreach 不会创建新线程,也不会"调动所有线程"。它使用线程池中有限数量的线程,并将任务提交给它们进行并行执行。在当前实现中,默认值为每个内核使用一个线程。

我认为你这样做的方式是错误的。从并行编程模式中,你会看到Parallel.ForEach只是真正的语法糖。

Parallel.ForEach 在很大程度上可以归结为这样的东西,

for (int p = 0; p < arrayStrings.Count(); p++)
{
    ThreadPool.QueueUserWorkItem(DoSomething(arrayStrings[p]);
}

线程池负责调度。 如果您有兴趣,有一些关于 ThreadPool 的调度程序在某种程度上如何表现的优秀文章,但这与 TPL 无关。

并行根本不

处理线程 - 它将任务调度到任务框架。然后有一个调度程序,默认调度程序转到线程池。这个将尝试找到一个 goo 线程数(在 4.5 中比在 4.0 中更好),线程池可能会慢慢启动新线程。

但这并不是 parallel.foreach ;)

Parallel.ForEach 将创建任何新的线程???

它永远不会。正如我所说 - 它有 1000 个 foreach,然后它排队 10.000 个任务,点。任务工厂调度程序将执行其编程要执行的操作((您可以替换它)。通常,默认 - 是的,慢慢地,新线程会在合理范围内涌现。