Thread.Start() 与 ThreadPool.QueueUserWorkItem() 的区别

50

微软.NET基类库提供了几种创建和启动线程的方法。基本上,调用方式与提供相同服务的其他方式非常相似:创建一个表示执行流(或多个执行流)的对象,为其分配一个表示要执行的执行流的委托,并根据委托签名,最终可能需要一个对象作为参数。

实际上有两种方法:

1)使用 System.Threading.Thread 类。

Thread curr = new Thread(myfunction); /* In a class, myfunction is a void taking an object */
curr.Start(new Object()); /* Or something else to be downcast */

2)使用 System.Threading.ThreadPool 类。

ThreadPool.QueueUserWorkItem(myfunction, new Object()); /* Same philosophy here */

我是否应该使用1)或2)?有什么特殊原因吗?

  • 性能原因?
  • 设计模式?
  • 最佳实践是什么?

我感觉答案是:“取决于情况”。您能否列举一些情况,在这些情况下一个方法比另一个更好?


https://dev59.com/u3I_5IYBdhLWcg3wJfgM#1507337 - Matt Davis
7个回答

57

启动新线程可能会是一个非常昂贵的操作。线程池可重用线程,因此可以分摊成本。除非您需要专用线程,否则建议使用线程池。通过使用专用线程,您可以更好地控制线程特定属性,例如优先级、文化等。此外,不应在线程池上执行长时间运行的任务,因为这将迫使池生成额外的线程。

除了您提到的选项外,.NET 4还提供了一些出色的并发抽象。请查看Task和Parallel类以及所有新的PLINQ方法。


3
有没有一个好的参考资料展示如何“观察”线程池,以便在所有项目完成/跟踪进度时使用? - jocull

13

托管线程池(The Managed Thread Pool)提供了一些很好的指南,告诉我们什么时候不应该使用线程池。

在我的经验中,当你需要一个持久的、专用的、长时间运行的线程时,你就需要创建自己的线程。对于其他所有情况,请使用异步委托或类似于QueueUserWorkItemBackgroundWorker或.NET 4.0的任务(Task)相关特性。


7
在.NET 4.5.2中,他们添加了一个新的方法:HostingEnvironment.QueueBackgroundWorkItem
这似乎是ThreadPool.QueueUserWorkItem的替代品。两者的行为类似,但在ASP.NET中使用新方法有一些好处:
HostingEnvironment.QueueBackgroundWorkItem方法让您安排小型后台工作项。ASP.NET跟踪这些项,并防止IIS在所有后台工作项完成之前突然终止工作进程。此方法不能在ASP.NET托管应用程序域之外调用。

7

2
使用线程池,您对线程系统的控制较少。这是为了简化处理过程而进行的折衷。如果您从线程池中获得所需的一切,可以自由地利用它。如果您需要更多对线程的控制,则需要使用Thread类。

1

ThreadPool.QueueUserWorkItem() 主要用于“发射并忘记”场景,即应用程序不依赖于操作是否完成。

对于细粒度控制,请使用经典线程。


1

除非以下情况,否则应使用 ThreadPool.QueueUserWorkItem

  • 您需要一个前台线程。

  • 您需要一个具有特定优先级的线程。

  • 您有导致线程长时间阻塞的任务。线程池具有最大线程数,因此大量被阻塞的线程池线程可能会防止任务启动。

  • 您需要将线程放置在单线程公寓中。所有线程池线程都位于多线程公寓中。

  • 您需要与线程关联稳定的身份,或者专门为任务分配线程。

参考 链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接