【问题标题】:Is a Task lightweight compared to a Thread?与线程相比,任务是轻量级的吗?
【发布时间】:2013-06-14 14:35:11
【问题描述】:

我无意中听到一位同事说任务基本上是一个轻量级线程。来自 C++ 背景(线程是最轻量的处理单元),这对我来说似乎违反直觉。

任务不是和线程一样重吗?

【问题讨论】:

标签: multithreading c#-4.0


【解决方案1】:

您需要将工作单元(任务)与用于托管/执行它们的底层进程区分开来。任务甚至不需要在其他线程上运行。例如,可以在单线程应用程序中执行任务,该应用程序会定期将控制权交给任务池。

即使Task在不同的线程上执行,Task和Thread之间通常也不是一对一的关系。线程作为池的一部分被预先分配,然后任务被安排在可用的这些线程上运行。创建新任务不需要创建线程的开销,它只需要任务队列中的 enque 成本。

这使得任务本质上更具可扩展性。在我的应用程序的整个生命周期中,我可以有数百万个任务,但实际上只能使用一些恒定数量的线程。

【讨论】:

  • “这使得任务本质上更具可扩展性。”应该注意的是,这不是Task 独有的。较旧的 ThreadPool 类提供与 IO 控制端口相同的可扩展性,或者仅实现具有固定数量消费者 Threads 的生产者/消费者队列。
  • @mbeckish 你是对的,我相信标准实现正是如此,在 ThreadPool 之上的易用层。
【解决方案2】:

通常,“线程”意味着强制并发。启动线程需要为其分配堆栈和内部 OS 数据结构。相比之下,“任务”通常指的是并发性是可选的工作,因此并行框架(例如 OpenMP、Cilk Plus、TBB、PPL)可以使用相同的线程来执行许多任务,通过序列化任务,并仅在必要时将可选并行性转换为真正的并行性以保持机器繁忙。

【讨论】:

  • 我对“强制并发”的想法很感兴趣。如果您的线程数多于内核数,您将在某个级别对它们进行序列化。强制并发的思想如何适用于大量线程。 (我可以从其他答案的上下文中理解您在说什么。这个术语只是令人困惑。)
  • @Richard - .NET Thread 对象的成本与内核数无关,甚至与托管线程映射到的内核线程数无关。成本是 1) 每个 Thread 对象分配大约 1 MB 内存,以及 2) 当线程多于内核时,上下文切换的 CPU 成本。 TaskThreadPool 提供的节省是您的应用程序可以创建 1000 个 Task 对象,而 .NET 可能只使用 20 个 Thread 对象来处理它们。
  • 更准确地说,“当您拥有比内核更多的就绪线程时,上下文切换的 CPU 成本”。
  • @mbeckish 我对线程的成本并不感到困惑,而是线程如何暗示强制并发。如果您的线程多于核心,它们将被切换进出,这意味着它们(至少部分地)按顺序执行。对我来说,“强制并发”一词似乎是用词不当或误解。
  • @Richard - 我很确定“强制并发”的意思是“为 N 个并发任务同时创建 N 个线程对象”,这与“同时为N 个并发任务”。换句话说,如果开发人员不打算手动限制并发工作,而是想立即设置所有要安排的任务,那么不好的选择是为您的 N 个任务创建 N 个Thread 对象,而好的选择是创建 N 个Task 对象,在幕后将使用少于 N 个Thread 对象。
【解决方案3】:

你是对的 - 一切都在幕后的线程上运行。

人们之所以说TaskThread 更轻量级的原因是,Microsoft 花了很多心思让Tasks 有效利用Threads,而且实现可能要轻得多重量比普通开发人员使用Thread 类自己想出的重量。

编辑

更明确的解释是Task 对象比Thread 对象更轻,虽然每个Task 最终都在Thread 上运行,但同时创建N 个Task 对象会导致小于N 个并发 Thread 正在使用的对象,对于大 N。

【讨论】:

    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 2017-05-06
    • 1970-01-01
    • 2020-07-16
    • 2012-04-06
    • 1970-01-01
    • 2019-05-10
    • 2011-02-01
    相关资源
    最近更新 更多