ThreadPool.QueueUserWorkItem 与 Task.Factory.StartNew
下面
ThreadPool.QueueUserWorkItem
和
Task.Factory.StartNew
如果上面的代码对于某个长时间运行的任务被调用 500 次,是否意味着所有线程池线程都将被占用?
或者 TPL(第二个选项)是否足够聪明,只占用少于或等于处理器数量的线程?
What is difference between the below
ThreadPool.QueueUserWorkItem
vs
Task.Factory.StartNew
If the above code is called 500 times for some long running task, does it mean all the thread pool threads will be taken up?
Or will TPL (2nd option) be smart enough to just take up threads less or equal to number of processors?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果要使用 TPL 启动长时间运行的任务,则应指定
TaskCreationOptions.LongRunning
,这意味着它不会将其调度到线程池上。 (编辑:正如评论中所指出的,这是一个特定于调度程序的决定,并不是一个硬性和快速的保证,但我希望任何明智的生产调度程序都会避免调度长时间运行的任务在线程池上。)您绝对不应该自己在线程池上安排大量长时间运行的任务。我相信现在线程池的默认大小相当大(因为它经常以这种方式被滥用),但从根本上讲,它不应该这样使用。
线程池的目的是避免短任务因创建新线程而受到与实际运行时间相比的较大影响。如果任务将运行很长时间,那么创建新线程的影响无论如何都会相对较小 - 并且您不希望最终可能耗尽线程池线程。 (现在不太可能,但我确实在早期版本的.NET 上经历过这种情况。)
就我个人而言,如果可以选择,我肯定会使用 TPL,因为
Task API 非常好 - 但一定记得告诉 TPL 您希望任务运行很长时间。
编辑:如评论中所述,另请参阅 PFX 团队的博客文章 在 TPL 和线程池之间进行选择:
编辑:同样从评论中,不要忘记 TPL 允许您使用 自定义调度程序,如果你真的想...
If you're going to start a long-running task with TPL, you should specify
TaskCreationOptions.LongRunning
, which will mean it doesn't schedule it on the thread-pool. (EDIT: As noted in comments, this is a scheduler-specific decision, and isn't a hard and fast guarantee, but I'd hope that any sensible production scheduler would avoid scheduling long-running tasks on a thread pool.)You definitely shouldn't schedule a large number of long-running tasks on the thread pool yourself. I believe that these days the default size of the thread pool is pretty large (because it's often abused in this way) but fundamentally it shouldn't be used like this.
The point of the thread pool is to avoid short tasks taking a large hit from creating a new thread, compared with the time they're actually running. If the task will be running for a long time, the impact of creating a new thread will be relatively small anyway - and you don't want to end up potentially running out of thread pool threads. (It's less likely now, but I did experience it on earlier versions of .NET.)
Personally if I had the option, I'd definitely use TPL on the grounds that the
Task
API is pretty nice - but do remember to tell TPL that you expect the task to run for a long time.EDIT: As noted in comments, see also the PFX team's blog post on choosing between the TPL and the thread pool:
EDIT: Also from comments, don't forget that TPL allows you to use custom schedulers, if you really want to...
不,
ThreadPool
线程通过使用Task.Factory.StartNew
方法(或更现代的Task.Run
方法)。调用Task.Factory.StartNew
500 次(对于长时间运行的任务)肯定会使ThreadPool
饱和,并且会使其在很长一段时间内保持饱和。这不是一个好的情况,因为饱和的线程池会对在此 500 个启动任务期间也可能处于活动状态的任何其他独立回调、计时器事件、异步延续等产生负面影响。Task.Factory.StartNew
方法安排在TaskScheduler.Current
,默认为TaskScheduler.Default
,这是内部
ThreadPoolTaskScheduler
类。这是的实现
>ThreadPoolTaskScheduler.QueueTask
方法:可以看到任务的执行被调度在无论如何,线程池。
ThreadPool.UnsafeQueueCustomWorkItem
是ThreadPool
类的一个内部
方法,并且有一些细微差别(bool forceGlobal
))未公开暴露。但是,当线程池饱和时,它不会改变它的行为。目前,这种行为也不是特别复杂。线程注入算法只是每 1000 毫秒向池中注入一个新线程,直到饱和事件结束。1 当工作需求超过线程的当前可用性时,
ThreadPool
被称为饱和,并且阈值SetMinThreads
已达到,超过该值不再按需创建新线程。No, there is no extra cleverness added in the way the
ThreadPool
threads are utilized, by using theTask.Factory.StartNew
method (or the more modernTask.Run
method). CallingTask.Factory.StartNew
500 times (with long running tasks) is certainly going to saturate theThreadPool
, and will keep it saturated for a long time. Which is not a good situation to have, because a saturatedThreadPool
affects negatively any other independent callbacks, timer events, async continuations etc that may also be active during this 500-launched-tasks period.The
Task.Factory.StartNew
method schedules the execution of the suppliedAction
on theTaskScheduler.Current
, which by default is theTaskScheduler.Default
, which is theinternal
ThreadPoolTaskScheduler
class. Here is the implementation of theThreadPoolTaskScheduler.QueueTask
method:As you can see the execution of the task is scheduled on the
ThreadPool
anyway. TheThreadPool.UnsafeQueueCustomWorkItem
is aninternal
method of theThreadPool
class, and has some nuances (bool forceGlobal
) that are not publicly exposed. But there is nothing in it that changes the behavior of theThreadPool
when it becomes saturated¹. This behavior, currently, is not particularly sophisticated either. The thread-injection algorithm just injects one new thread in the pool every 1000 msec, until the saturation incident ends.¹ The
ThreadPool
is said to be saturated when the demand for work surpasses the current availability of threads, and the thresholdSetMinThreads
above which new threads are no longer created on demand has been reached.