【问题标题】:Under which circumstances should I use different .NET threading methods?在什么情况下我应该使用不同的 .NET 线程方法?
【发布时间】:2014-07-05 04:05:49
【问题描述】:

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

新线程 -> 何时需要一个额外的线程?

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

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

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

在 MSDN 上,并没有说明 new Thread 方法已过时??

【问题讨论】:

  • 别忘了微软的响应式框架。

标签: .net multithreading


【解决方案1】:

您对Threads 和ThreadPool 用法的基本理解或多或少是正确的。但是,Task 稍微复杂一些。

线程、线程池、任务

首先,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。这将是一团糟。

当然还有其他使用它们的原因,即对性能极为敏感的场景(我敢肯定还有更多,但我现在能想到的就这些了)。

使用任务安排工作

当您安排新的Task. 时,您对何时使用ThreadPool 或新的Thread 的考虑仍然适用。这里的指导方针很简单:

  • 如果预计任务会连续运行(在整个进程运行期间运行的任务就是一个很好的例子),最好在启动它时指定TaskCreationOptions.LongRunning 标志。这类似于开始一个新线程。
  • 如果您有一些有限的 CPU 密集型工作要异步执行,请将其通过 Task.Run 推送到 ThreadPool(它使用默认任务调度程序,恰好是当前的 ThreadPoolTaskScheduler执行)。这类似于使用ThreadPool.QueueUserWorkItem(WaitCallback)
  • 如果您的任务在其生命周期中的大部分时间都在执行异步 IO,那么您实际上不必担心它在哪里执行。

平行

现在让我们谈谈Parallel 类及其成员。首先,你有Parallel.Invoke,这大致相当于并行启动一堆任务,然后阻止它们完成。这仅在您处理要并行运行的多个阻塞操作时才真正有用。如果这是您的情况,并且您不想弄乱多个 Task 实例,请使用它。

Parallel.ForParallel.ForEach 是一个稍微不同的野兽,主要用于并行处理 CPU 密集型工作以处理 collection 的元素(将它们用于 IO 工作并不是一个好主意,因为他们内置的“负载平衡”的工作方式,最终产生了太多的线程,除非你限制并行度)。这些方法在概念上与ThreadThreadPoolTask 不同,更接近于PLINQ。在并行化集合元素处理会带来可衡量的性能提升的场景中,在通常使用 forforeach 循环的地方使用它们。

【讨论】:

  • 美丽。谢谢。
  • 对 Parrel.ForEach () 的一个注释我经常在必须进行批处理时使用它们。不幸的是,大多数时候我会阅读一些配置文件,进行密集计算并编写一个小的结果文件。不幸的是,调度程序为此启动了无数线程 - 一种避免这种情况的方法是简单地指定一个选项类并将 maxparallelism 设置为 processorcount -1。
  • @ChristianSauer,这是因为您在传递给 Parallel.ForEach 的委托主体内阻塞了异步 (IO) 操作,导致负载均衡器失控 -我在上一段中谈到的确切问题(您也碰巧使用了我提到的解决方法)。这在 Stephen Toub 的“并行编程模式”,第 33 页中进行了讨论 - 以及一个适当的修复,即在涉及 IO 绑定工作时避开Parallel.ForEach 并使用批量Tasks,最终导致更高效的资源使用和更好的可扩展性
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 2011-05-20
  • 2014-12-18
  • 2020-10-02
相关资源
最近更新 更多