使用Thread.Start和QueueUserWorkItem的优点

在multithreading的.NET编程中,使用ThreadPool.QueueUserWorkItem与通过新的Thread()和Thread.Start()启动我自己的线程有什么关系?

在一个服务器应用程序(比方说,一个ASP.NET应用程序或WCF服务),我认为ThreadPool总是在那里和可用。 怎么样在客户端应用程序,如WinForms或WPF应用程序? 是否有成本来启动线程池? 如果我只想要3或4个线程在一些计算上短时间工作,对QUWI或Thread.Start()更好。

ThreadPool总是在那里,但是,根据处理器的数量,分配给池的线程数量是有限的。 对于ASP.NET应用程序来说,使用线程通常是一个坏主意,除非你真的从一个Async进程开始,并且知道不会有大量的请求(请记住ThreadPool中有一定数量的线程与AppDomain中运行的所有内容共享,并且使用新的Thread()也可以创build线程总数的实际限制。

对于WinForms应用程序,可以考虑使用BackgroundWorker而不是使用Thread或ThreadPool。 它使用ThreadPool,但是,它使multithreading精明的程序员更容易在线程之间进行通信。

更新

避免使用ThreadPool.QueueUserWorkItem,因为您的应用程序池可能随时消失。 如果必须的话,将这项工作移到外面或使用WebBackgrounder。

从Hanselman.com – “清单:什么不在ASP.NET中”

我会做这个评论,但是太长了。
CodemonkeyKing似乎有一个重要的点,虽然不够强烈,在我看来。

有很多标准可以用来描述代码。 它会用在一个长期运行的应用程序或不? Winforms的应用程序或不? 它是一个服务器或客户端应用程序? 库或独立的EXE? 等等

在我看来,如果你的代码将运行在一个独立的应用程序中,并且你可以控制所有周围的代码,那么你可以推出自己的线程池,开始自己的线程,并测量和pipe理线程启动,线程延迟,和资源消耗。 或者你可以使用QUWI,但它不会杀死你的应用程序。 你可以自由select。

另一方面,如果你的代码打包成一个可能在服务器中使用的库 – 在ASP.NET中,或者在SQL CLR应用程序或WCF服务中 – 那么创build线程是一个非常糟糕的主意。 你需要使用QUWI或其他一些利用内置线程池的机制(如BackgroundWorker)。 如果要在其他库中使用客户端应用程序,则需要QUWI。 想象一下,每个想要利用多核计算机的图书馆都推出了自己的主题。 在使用多个库的应用程序中会出现完全的混乱。 猖獗的线程,都争夺相同的资源。 没有#threads vs#处理器的中央协调。

良好的卫生要求一个图书馆,无论是在客户端应用程序或服务器应用程序使用,使用公共线程池,这意味着QUWI。

最后要实现的是这个;

托pipe线程是后台线程或前台线程。 后台线程与前台线程相同,只有一个例外:后台线程不保持托pipe执行环境的运行。 一旦所有的前台线程在一个托pipe进程(其中.exe文件是托pipe程序集)中停止,系统将停止所有后台线程并closures。

属于托pipe线程池(即,其线程的IsThreadPoolThread属性为true)的线程是后台线程。 所有从非托pipe代码进入托pipe执行环境的线程都被标记为后台线程。 通过创build和启动一个新的Thread对象生成的所有线程默认情况下都是前台线程。

如果使用线程来监视某个活动(如套接字连接),请将其IsBackground属性设置为true,以便线程不会阻止进程终止。

从MSDN网站 。

这个讨论会让你感兴趣
什么时候不应该在.Net中使用ThreadPool?

如果任务很短,通过QueueUserWorkItem或BeginInvoke调度线程池上的任务,您可能会看到更好的性能,因为避免了创build和销毁自己线程的重复成本。

线程池显然支付创build线程,但它重用线程,所以你不支付每个任务的成本。

您可能还想看看C#中的Threading (免费电子书)。

这是使用ThreadPool.QueueUserWorkItem的另一个好处。

我有一个winforms应用程序,它有一个听觉。 我最初实施这个使用

heartbeat = new Thread(HeartbeatDoWork); heartbeat.Start(); 

98%的时间工作正常。 但是,当应用程序closures有时它不会正常死亡,即进程仍然可以在任务pipe理器中看到。

简而言之,心跳发表了一个事件,正在处理事件(CAB pub / sub),在那里“卡住”了。

修复很简单,只需要改用这个

  ThreadPool.QueueUserWorkItem(HeartbeatDoWork); 

我确信在拆分自己的线程时可以做一些额外的事情,并且可以正确清理,但是这更简单,更快,更容易理解…… ThreadPool为我完成所有工作。

线程池始终在.NET应用程序中可用,无论其types如何。

通过ThreadPool.QueueUserWorkItem从线程池启动一个线程的成本不会超过开始自己的线程的成本,并且可能会更less。

如果你只想在短时间内使用几个线程,那么就使用线程池。 这就是它的目的。

如果你正在创build自己的线程,那么首先启动它们是有意义的,然后让它们在一段时间内停留一段时间,以防再次需要它们 – 你让一个空闲的线程等待一个事件,以便通过信号重新激活它事件,经过足够长的时间后,线程会自行唤醒并退出,回收未使用的资源。 通过保留less量空闲线程准备好重用,可以减less不断创build和销毁线程的开销(这对于足够短暂的操作非常重要)。

但这基本上是线程队列已经为你做的 – 所以你可以使用它。 这是一个解决的问题。

在C ++社区中曾经发生过类似的关于string的争论,不pipe你信不信。 我们应该每个写我们自己的string类,还是应该使用std::string ? 答案取决于你是否想要学习如何编写一个string类,或者你已经完成了,你想继续发明新的东西。

使用Thread.Start()一个好处是, 你可以在以后故意中止线程。 在ThreadPool ,在排入队列之后,您无法控制线程的执行。

我的大部分阅读都赞同Marcel,如果有事情发生,控制线程的能力(Abort …)应该被认真考虑,尤其是在你处理第三方调用时,你无法控制并且可能会挂起。