什么决定了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个线程(基于观察)。 我的问题:
- 什么决定了一次使用的线程数?
- 我该如何找回这个号码?
- 我该如何更改此号码?
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
关于Threading
和TPL
系列文章。
如果将任务数量增加到例如1000000,您将看到随着时间的推移产生更多的线程。 TPL倾向于每500ms注入一个。
TPL线程池不了解IO绑定工作负载(睡眠是IO)。 在这些情况下依靠TPL选择正确的并行度并不是一个好主意。 TPL是完全无能为力的,并且基于对吞吐量的模糊猜测注入更multithreading。 还要避免死锁。
在这里,TPL策略显然没有用,因为您添加的线程越多,您获得的吞吐量就越多。 在这个设计的案例中,每个线程可以每秒处理一个项目。 TPL对此一无所知。 将线程数限制为核心数是没有意义的。
什么决定了一次使用的线程数?
几乎没有记录TPL启发式。 他们经常出错。 特别是在这种情况下,它们会随着时间的推移产生无限数量的线程 。 使用任务管理器自己查看。 让它运行一个小时,你将有1000个线程。
我该如何找回这个号码? 我该如何更改此号码?
您可以检索其中一些数字,但这不是正确的方法。 如果需要保证DOP,可以使用AsParallel().WithDegreeOfParallelism(...)
或自定义任务调度程序。 您还可以手动启动LongRunning
任务。 不要混淆过程全局设置。