C# - 线程池与任务

Posted

技术标签:

【中文标题】C# - 线程池与任务【英文标题】:C# - ThreadPool vs Tasks 【发布时间】:2010-12-18 23:33:45 【问题描述】:

正如一些人可能在 .NET 4.0 中看到的那样,他们添加了一个新的命名空间 System.Threading.Tasks,这基本上就是一个任务。从使用 ThreadPool 开始,我只使用了几天。

哪一个更高效,资源消耗更少? (或者只是整体更好?)

【问题讨论】:

我认为任务使用了线程池。 【参考方案1】:

Tasks 命名空间的目标是提供一种可插拔的架构,使多任务应用程序更易于编写和更灵活。

该实现使用TaskScheduler 对象来控制任务的处理。这具有您可以覆盖以创建自己的任务处理的虚拟方法。方法包括例如

protected virtual void QueueTask(Task task)
public virtual int MaximumConcurrencyLevel

使用默认实现会有很小的开销,因为 .NET 线程实现有一个包装器,但我不认为它会很大。

有一个自定义 TaskScheduler 的(草稿)实现,它在单个线程 here 上实现多个任务。

【讨论】:

非常正确,但我不认为 Task 类的主要目的或吸引力在于自定义调度程序。这是一个非常专业的功能,在某些情况下非常宝贵,但大多数用户永远不会碰它。 默认 TaskScheduler 只使用核心 .NET Framework 线程池msdn.microsoft.com/en-us/library/…【参考方案2】:

哪一种效率更高,效率更低 消耗资源?

无关紧要,差别很小。

(或者只是整体更好)

Task 类将更易于使用,因为它为启动和加入线程以及传输异常提供了一个非常干净的接口。它还支持(有限的)负载平衡形式。

【讨论】:

任务是在 .NET 中使用 Parallel 框架的一种更简单的方法。线程直接取自操作系统并立即运行,消耗更多的 cpu 时间,并且通常不允许框架管理上下文切换和优化技术。这类似于一个孩子发脾气并尖叫我现在就想要! vs 等待轮到他们的人。 @MickeyPerlstein。在我看来,您对使用线程的描述并不真正适用于线程池(线程池类),这是 OP 所要求的。我同意任务是利用多个 CPU 的更有效方式,尤其是对于高级场景,但对于简单的场景ThreadPool.QueueUserWorkItem 似乎完全足够了。评论? @ToolmakerSteve 使用一个系统,好好学习。任务是 ms 推荐的。 QueueUserWorkItem 有太多的怪癖,这就是他们首先发明任务的原因。这是“承诺”范式【参考方案3】:

“从 .NET Framework 4 开始,TPL 是编写多线程和并行代码的首选方式。”

http://msdn.microsoft.com/en-us/library/dd460717.aspx

【讨论】:

【参考方案4】:

线程

裸机的东西,你可能不需要使用它,你可能可以使用 LongRunning 任务并从它的设施中受益。

任务

线程之上的抽象。它使用线程池(除非您将任务指定为LongRunning 操作,如果是这样,则会在后台为您创建一个新线程)。

线程池

顾名思义:线程池。 .NET 框架是否为您处理有限数量的线程。为什么?因为在只有 8 个内核的 CPU 上打开 100 个线程来执行昂贵的 CPU 操作绝对不是一个好主意。框架将为您维护这个池,重用线程(不是在每个操作中创建/杀死它们),并以您的 CPU 不会烧毁的方式并行执行其中的一些。

好的,但是什么时候使用每个?

在简历中:始终使用任务。

Task 是一个抽象,所以它更容易使用。我建议您始终尝试使用任务,如果您遇到一些需要自己处理线程的问题(可能有 1% 的时间),那么请使用线程。

但请注意:

I/O 绑定:对于 I/O 绑定操作(数据库调用、读/写文件、API 调用等)永远不要使用普通任务,请使用LongRunning 任务或如果你需要线程,但不是正常的任务。因为它会引导你进入一个线程池,其中有几个线程很忙,还有很多其他任务等待轮到它占用池。 CPU 绑定:对于 CPU 绑定操作,只需使用正常任务即可。

【讨论】:

您关于避免 I/O 任务的建议是完全错误的。任务非常适合 I/O 绑定操作,并受到 asyncawait 的鼓励。 即使是 asyncawait 的相关主题,这里也没有讨论。但是,无论如何,他们不支持 I/O 操作的“线程池线程”(正常任务)。我建议您查看@StephenCleary 的this 答案,他在其中更详细地介绍了它。在他的两个示例中,他都没有使用Task.Run()(这会产生在另一个上下文中执行的线程池线程)。其他答案也很有帮助。 增加池大小会解决 IO 绑定问题吗? @jw_ 我不这么认为,而且 IMO 似乎有风险。但只是做一些测试来看看效果。 @fabriciorissetto 我看不出您建议的答案与“使用LongRunning 任务进行I/O”有什么关系,ConfigureAwait 只会导致CPU 绑定在I/O 上执行/O-bound 线程,它仍然来自一个 Task 线程池线程..【参考方案5】:

调度是并行任务的一个重要方面。

与线程不同,新任务不一定会立即开始执行。相反,它们被放置在工作队列中。任务在其关联的任务调度程序将它们从队列中删除时运行,通常是在内核可用时。任务调度器试图通过控制系统的并发程度来优化整体吞吐量。只要有足够多的任务并且这些任务完全没有序列化依赖项,程序的性能就会随着可用内核的数量而扩展。这样,任务就体现了潜在并行的概念

正如我在 msdn 上看到的 http://msdn.microsoft.com/en-us/library/ff963549.aspx

【讨论】:

ThraPool 也是如此,那么它们又有什么不同呢?【参考方案6】:

ThreadPoolTask的区别很简单。 要了解任务,您应该了解线程池。

ThreadPool 基本上有助于管理和重用空闲线程。在 换句话说,线程池是后台线程的集合。

任务的简单定义可以是:

Task工作异步管理工作单元。用简单的话 任务不会创建新线程。相反,它有效地管理 线程池的线程。Tasks 由 TaskScheduler 执行,TaskScheduler 将任务排队到线程中。

【讨论】:

【参考方案7】:

另一个需要考虑的关于任务的好点是,当您使用 ThreadPool 时,您没有任何方法可以中止或等待正在运行的线程(除非您在线程方法中手动执行),但是 使用任务是可能的。如有错误请指正

【讨论】:

AFAIK 您无法中止任务,您可以尝试取消它,但根据我的经验,这并不总是有效......例如 Thread.Abort() - 一个总是有效的:) 其实你可以使用cancellationtoken来取消线程池和任务。通过c#查看clr

以上是关于C# - 线程池与任务的主要内容,如果未能解决你的问题,请参考以下文章

何时在 C# 中使用线程池? [关闭]

线程池与Threadlocal

线程池与Threadlocal

Python 37 进程池与线程池 协程

java线程池与tomcat线程池策略算法上的区别

python之进程池与线程池