【问题标题】:Difference between Task (System.Threading.Task) and Thread任务(System.Threading.Task)和线程之间的区别
【发布时间】:2012-02-29 04:00:33
【问题描述】:

据我了解,任务和线程之间的区别是任务发生在线程池中,而线程是我需要自己管理的东西..(并且该任务可以取消并返回到线程-在他的任务结束时游泳池)

但是在一些博客中我读到如果操作系统需要创建任务和创建线程 => 创建(和销毁)任务会更容易。

有人可以解释一下为什么创建任务很简单吗?

(或者我在这里遗漏了一些东西......)

【问题讨论】:

  • jobworker 有什么区别?一个工人一份工作;工人不是一份工作。有些工作是由一个工人完成的;一些工作被分解成更小的工作,所有这些工作都是由许多工人一起完成的。与任务和线程相同;任务不是一种线程;一个任务就是一个工作,而一个线程是一个完成工作的工作者。
  • 我听到的另一个类比是处理器是驱动程序,线程是卡车,而任务是需要拖拉的负载。一个司机(处理器)一次只能操作一辆卡车(线程),一辆卡车(线程)一次只能拖运一个负载(任务)。您可以购买任意数量的卡车,但司机在卡车之间切换的时间越多,他们驾驶的时间就越少。货物可以堆积在仓库中等待运输,仓库可以根据任何合理的规则对它们进行优先排序并分配给卡车。

标签: c# .net task-parallel-library


【解决方案1】:

认为当你说任务时你在谈论的是System.Threading.Task。如果是这种情况,那么您可以这样考虑:

  • 一个程序可以有多个线程,但一个处理器内核一次只能运行一个线程。
    • 线程非常开销很大,在运行的线程之间切换也很开销。
    • 所以... 让数千个线程做事是低效的。想象一下,如果您的老师有 10,000 个任务要做。你会花很多时间在它们之间循环,以至于你永远也做不成任何事情。如果启动过多线程,CPU 也会发生同样的情况。

为了解决这个问题,.NET 框架允许您创建任务。任务是捆绑到一个对象中的一些工作,它们允许您做一些有趣的事情,例如捕获该工作的输出并将工作的各个部分链接在一起(首先去商店,然后买一本杂志)。

任务安排在线程池上。具体的线程数取决于使用的调度程序,但默认调度程序会尝试选择最适合您拥有的 CPU 内核数量以及任务实际使用 CPU 时间的时间的线程数。如果您愿意,您甚至可以编写自己的调度程序来执行特定的操作,例如确保该调度程序的所有任务始终在单个线程上运行。

因此,将任务视为待办事项列表中的项目。你可能一次可以做 5 件事,但如果你的老板给你 10000 个,它们会堆积在你的收件箱中,直到你正在做的前 5 件事完成。 Tasks 和 ThreadPool 之间的区别在于,Tasks(正如我之前提到的)让您可以更好地控制不同工作项之间的关系(想象一下将多条指令装订在一起的待办事项),而 ThreadPool 只允许您排队一堆单独的单阶段项目(函数)。

【讨论】:

  • Tasks 默认安排在 ThreadPool 上,但并非必须如此。如果您使用TaskCompletionSource 创建它,甚至可能没有任何代码与Task 直接关联。
【解决方案2】:

您听到了两种不同的任务概念。第一个是作业的概念,第二个是过程的概念。

很久以前(用计算机术语),没有线程。程序的每个运行实例都称为一个进程,因为它只是一步一步地执行,直到退出。这与将流程视为一系列步骤的直观概念相匹配,例如工厂装配线。操作系统管理进程抽象。

然后,开发人员开始向工厂添加多条装配线。现在一个程序可以一次做不止一件事,一个库或(今天更常见的)操作系统将管理每个线程中步骤的调度。线程是一种轻量级进程,但线程属于一个进程,一个进程中的所有线程共享内存。另一方面,多个进程不能混淆彼此的内存。因此,Web 服务器中的多个线程每个都可以访问有关连接的相同信息,但 Word 无法访问 Excel 的内存中数据结构,因为 Word 和 Excel 作为单独的进程运行。将进程视为一系列步骤的想法与线程的进程模型并不真正匹配,因此有些人开始将“以前称为进程的抽象”称为任务。这是您在博客文章中看到的第二个任务定义。请注意,很多人仍然使用过程这个词来表示这件事。

嗯,随着线程变得越来越普遍,开发人员在它们之上添加了更多抽象,以使它们更易于使用。这导致了线程池的兴起,这是一个库管理的线程“池”。您向库传递一个作业,然后库选择一个线程并在该线程上运行该作业。 .NET 框架有一个线程池实现,当您第一次听说“任务”时,文档实际上意味着您传递给线程池的作业。

所以从某种意义上说,文档和博客文章都是正确的。任务一词的超载是令人遗憾的混乱来源。

【讨论】:

  • +1 这是一个很好的解释。我认为 OP 正在谈论System.Threading.Task。如果您可以稍微扩展讨论以包括 TPL 的细微差别,那就太好了。
【解决方案3】:

线程从 v1.0 开始成为 .Net 的一部分,在 .Net 4.0 中发布的任务并行库 TPL 中引入了任务。

您可以将任务视为线程的更复杂版本。它们非常易于使用,并且比 Threads 有很多优势,如下所示:

  1. 您可以为任务创建返回类型,就好像它们是函数一样。
  2. 您可以使用“ContinueWith”方法,该方法将等待上一个任务然后开始执行。 (抽象等待)
  3. 根据我公司的指导方针,应避免使用抽象锁。
  4. 您可以使用 Task.WaitAll 并传递一组任务,这样您就可以等到所有任务都完成。
  5. 您可以将任务附加到父任务,从而决定先存在父任务还是子任务。
  6. 您可以使用 LINQ 查询实现数据并行。
  7. 您可以创建并行 for 和 foreach 循环
  8. 非常容易处理任务异常。
  9. *最重要的是,如果在单核机器上运行相同的代码,它只会充当一个进程,而不会产生任何线程开销。

任务相对于线程的缺点:

  1. 您需要 .Net 4.0
  2. 学过操作系统的新人可以更好地理解线程。
  3. 刚接触框架,所以没有太多帮助。

一些提示:- 始终使用语义完美且标准的 Task.Factory.StartNew 方法。

查看任务并行库以获取更多信息 http://msdn.microsoft.com/en-us/library/dd460717.aspx

【讨论】:

    【解决方案4】:

    扩展 Eric Lippert 的评论:

    Threads 是一种允许您的应用程序并行执行多项操作的方法。例如,您的应用程序可能有一个线程处理来自用户的事件,例如按钮单击,另一个线程执行一些长时间的计算。这样,您可以“同时”做两件不同的事情。如果您不这样做,则用户在计算完成之前不会单击按钮。所以,Thread 是可以执行你写的一些代码的东西。

    Task,另一方面代表某种工作的抽象概念。该作业可能有结果,您可以等到作业完成(通过调用Wait())或说您想在作业完成后做某事(通过调用ContinueWith())。

    您要表示的最常见的工作是与当前代码并行执行一些计算。 Task 为您提供了一种简单的方法来做到这一点。代码实际运行的方式和时间由TaskScheduler 定义。默认使用ThreadPool:一组可以运行任何代码的线程。这样做是因为创建和切换线程效率低下。

    Task 不必直接与某些代码相关联。您可以使用TaskCompletionSource 创建一个Task,然后随时设置其结果。例如,您可以创建一个Task,并在用户单击按钮时将其标记为已完成。其他一些代码可以在该Task 上等待,而在它等待时,没有为该Task 执行任何代码。

    如果您想知道何时使用Task 以及何时使用ThreadTask 比创建自己的Threads 更易于使用且更高效。但有时,您需要比Task 提供的更多控制。在这些情况下,直接使用Thread 是有意义的。

    【讨论】:

      【解决方案5】:

      任务实际上只是手动启动线程的样板代码的包装器。从根本上来说,没有区别。任务只是使线程的管理更容易,而且由于样板噪音的减少,它们通常更具表现力。

      【讨论】:

      • 克里斯,你能解释一下安排的任务吗?
      • 我会添加一个答案,但我也会将您定向到@AdamMihalcin 的答案
      • @ChrisShain 我同意任务被安排在线程上,并不是要暗示线程与任务相同,而是实现了相同的开发目标。可能我的解释有点笼统……
      • @JustinPihony 是的,它只是让不知情的读者得出错误的结论。我认为编辑更准确。 +1
      猜你喜欢
      • 1970-01-01
      • 2011-03-16
      • 2015-05-08
      • 2011-03-03
      • 1970-01-01
      • 2016-06-22
      • 1970-01-01
      • 2010-12-28
      • 1970-01-01
      相关资源
      最近更新 更多