在哪种情况下我应该使用不同的.NET线程方法?

时间:2014-07-05 04:05:50

标签: .net multithreading

我一直在研究处理多线程.NET应用程序的不同方法。它变得有点混乱。

新主题 - >当需要一个额外的线程时?

ThreadPool - >当你需要多个线程时。使用现有线程并将优化(基于所涉及的工作的数量)留给框架更便宜。

任务 - >当你想要一个额外的线程,并且你碰巧使用.net 4.0或更高版本。这是新线程的API吗?

Parallel.for - >当您有多个任务并希望框架处理优化时,根据CPU核心数分配不同任务的工作。

在MSDN上,它没有声明新线程方法已经过时了?

1 个答案:

答案 0 :(得分:7)

您对ThreadThreadPool用法的基本了解或多或少是正确的。然而,Task稍微复杂一些。

线程,ThreadPool,任务

首先,Task只表示异步操作。它可以在ThreadPool线程上执行(如果您是调度任务的人,那么通常它会在大多数情况下使用TaskScheduler.Default)。它也可以在单独的非线程池线程上执行(当您指定TaskCreationOptions.LongRunning时)。它甚至可以在没有线程的情况下执行(想想异步IO操作:http://blog.stephencleary.com/2013/11/there-is-no-thread.html)。最后,当Task封装多个异步操作时,它可以是上述任何一个的组合。

最终TaskThreadThreadPool上添加了一个抽象层(适当时它会在内部使用,当您安排任务时,您可以对其进行一定程度的控制)。在三种线程方法Task中,由于围绕它构建语言支持,它可能是您的首选武器。使用Task编写异步操作序列与使用低级线程工具执行相同操作相比是轻而易举的。

但这并不意味着ThreadThreadPool 真正已过时 - 仅仅是因为它们不应成为您进行新开发的第一个要点。撇开即使您使用Task时框架仍然使用它们的事实,请考虑使用ThreadThreadPool的全功能生产代码的数量,一旦这些类型中的任何一个用ObsoleteAttribute标记,会发生什么。这将是一团糟。

当然也有其他理由使用它们,即对性能非常敏感的场景(我确信还有更多,但这就是我现在所能想到的)。

使用任务计划工作

关于何时使用ThreadPool或新Thread的考虑因素仍然适用于您安排新Task.时此处的指南很简单:

  • 如果期望任务连续运行(运行整个过程运行的任务就是一个很好的例子),最好在启动它时指定TaskCreationOptions.LongRunning标志。这类似于开始一个新线程。
  • 如果您想要异步执行一些有限的CPU限制工作,请通过ThreadPool将其推送到Task.Run(它使用默认任务调度程序,恰好是{{} 1}}在当前的实现中)。这类似于使用ThreadPoolTaskScheduler
  • 如果您的任务大部分时间都在执行异步IO,那么您并不需要担心它的执行位置。

<强>并行

现在让我们谈谈ThreadPool.QueueUserWorkItem(WaitCallback)类及其成员。首先,你有Parallel,这大致相当于并行启动一堆任务,然后在完成时阻塞。这在您处理要并行运行的多个阻塞操作时才非常有用。如果这是你的情况而且你不想搞乱多个Parallel.Invoke实例,那就使用它。

TaskParallel.For是一个略有不同的野兽,主要用于并行化集合元素的CPU绑定工作(使用它们不是一个好主意)对于IO工作,由于它们内置的“负载平衡”的工作方式,最终产生太多的线程,除非你限制并行度)。这些方法在概念上与Parallel.ForEachThreadThreadPool不同,并且更接近PLINQ。在通常使用Taskfor循环的情况下使用它们,在这种情况下,并行化集合元素处理会带来可衡量的性能提升。