什么决定了TaskFactory衍生作业的线程数?

我有以下代码:

var factory = new TaskFactory(); for (int i = 0; i  foo(i1)); } static void foo(int i) { Thread.Sleep(1000); Console.WriteLine($"foo{i} - on thread {Thread.CurrentThread.ManagedThreadId}"); } 

我可以看到它一次只做4个线程(基于观察)。 我的问题:

  1. 什么决定了一次使用的线程数?
  2. 我该如何找回这个号码?
  3. 我该如何更改此号码?

PS我的盒子有4个核心。

PPS我需要具有特定数量的任务(而不是更多)由TPL同时处理并最终得到以下代码:

 private static int count = 0; // keep track of how many concurrent tasks are running private static void SemaphoreImplementation() { var s = new Semaphore(20, 20); // allow 20 tasks at a time for (int i = 0; i  { try { s.WaitOne(); Interlocked.Increment(ref count); foo(i1); } finally { s.Release(); Interlocked.Decrement(ref count); } }, TaskCreationOptions.LongRunning); } } static void foo(int i) { Thread.Sleep(100); Console.WriteLine($"foo{i:00} - on thread " + $"{Thread.CurrentThread.ManagedThreadId:00}. Executing concurently: {count}"); } 

当你在.NET中使用Task时,你告诉TPL安排一个工作(通过TaskScheduler )在ThreadPool上执行。 请注意,工作将尽早安排,但调度程序认为合适。 这意味着TaskScheduler将决定将使用多少线程来运行n个任务以及在哪个线程上执行哪个任务。

TPL经过精心调整,并在执行任务时继续调整算法。 因此,在大多数情况下,它会尽量减少争用。 这意味着如果您运行100个任务并且只有4个核心(可以使用Environment.ProcessorCount获得),那么在任何给定时间执行4个以上的线程都没有意义,否则它将需要做更多上下文切换。 现在有时您希望显式覆盖此行为。 让我们说在你需要等待某种IO完成的情况下,这是一个完全不同的故事

总之,请相信TPL。 但是如果你坚持每个任务产生一个线程(并不总是一个好主意!),你可以使用:

 Task.Factory.StartNew( () => /* your piece of work */, TaskCreationOptions.LongRunning); 

这告诉Default Taskscheduler明确生成一个新线程。

您也可以使用自己的Scheduler并将其传递给TaskFactory 。 你可以在HERE找到一大堆Schedulers

注意另一个替代方法是使用PLINQ ,它再次默认分析您的查询并决定是否并行化它会产生任何好处,再次在阻塞IO的情况下,您确定启动多个线程将导致更好的执行,您可以通过使用WithExecutionMode(ParallelExecutionMode.ForceParallelism)强制并行,然后你可以使用WithDegreeOfParallelism来提供有关使用多少线程的提示, 但是请记住, 不能保证你会得到那么multithreading,如MSDN所说:

设置要在查询中使用的并行度。 并行度是将用于处理查询的并发执行任务的最大数量

最后,我强烈建议阅读THIS关于ThreadingTPL系列文章。

如果将任务数量增加到例如1000000,您将看到随着时间的推移产生更多的线程。 TPL倾向于每500ms注入一个。

TPL线程池不了解IO绑定工作负载(睡眠是IO)。 在这些情况下依靠TPL选择正确的并行度并不是一个好主意。 TPL是完全无能为力的,并且基于对吞吐量的模糊猜测注入更multithreading。 还要避免死锁。

在这里,TPL策略显然没有用,因为您添加的线程越多,您获得的吞吐量就越多。 在这个设计的案例中,每个线程可以每秒处理一个项目。 TPL对此一无所知。 将线程数限制为核心数是没有意义的。

什么决定了一次使用的线程数?

几乎没有记录TPL启发式。 他们经常出错。 特别是在这种情况下,它们会随着时间的推移产生无限数量的线程 。 使用任务管理器自己查看。 让它运行一个小时,你将有1000个线程。

我该如何找回这个号码? 我该如何更改此号码?

您可以检索其中一些数字,但这不是正确的方法。 如果需要保证DOP,可以使用AsParallel().WithDegreeOfParallelism(...)或自定义任务调度程序。 您还可以手动启动LongRunning任务。 不要混淆过程全局设置。