在多线程.NET编程中,使用ThreadPool.QueueUserWorkItem与通过新的Thread()和Thread.Start()启动自己的线程的决策标准是什么?
在服务器应用程序(比方说,ASP.NET应用程序或WCF服务)中,我认为ThreadPool始终存在且可用。在客户端应用程序中,如WinForms或WPF应用程序怎么样?启动线程池有成本吗?如果我只想让3或4个线程在一些计算上短时间工作,那么QUWI或Thread.Start()会更好吗?
答案 0 :(得分:21)
ThreadPool始终存在,但是,根据处理器的数量,为池分配了有限数量的线程。对于ASP.NET应用程序,使用线程通常是一个坏主意,除非你真的开始使用Async进程并且知道不会有大量请求(请记住ThreadPool中有一定数量的线程)您与AppDomain中运行的所有内容共享,并且您希望使用新的Thread()创建的线程总数也有实际限制。
对于WinForms应用程序,请考虑使用BackgroundWorker而不是使用Thread或ThreadPool。它使用ThreadPool,但它使多线程精明的程序员更容易在线程之间进行通信。
<强>更新强>
避免使用 ThreadPool.QueueUserWorkItem ,因为您的应用池可能随时消失。如果必须,可以将此工作移到外面或使用WebBackgrounder。
答案 1 :(得分:14)
我要发表评论,但是时间过长了 CodemonkeyKing似乎已经发挥了重要作用,但在我看来并不够强烈。
您可能会使用许多标准来描述代码。它会在长时间运行的应用程序中使用吗? Winforms app或不?它是服务器或客户端应用程序吗?库或独立的exe?等等。
在我看来,如果您的代码将在独立应用程序中运行并且您可以控制所有周围的代码,那么您可以滚动自己的线程池,启动自己的线程,并测量和管理线程启动周围的成本,线程延迟和资源消耗。或者你可以使用QUWI,但它不会以任何方式杀死你的应用程序。你可以自由选择。
另一方面,如果您的代码打包为可以在服务器中使用的库 - 在ASP.NET中,或者在SQL CLR应用程序或WCF服务中 - 那么创建它是一个非常糟糕的主意线程。您需要使用QUWI或其他一些利用内置线程池的机制(如BackgroundWorker)。如果要在客户端应用程序中与其他库一起使用,则需要再次使用QUWI。想象一下,每个想要利用多核计算机的图书馆都会推出自己的线程。在使用多个库的应用程序中会出现完全混乱。猖獗的线程,都在竞争相同的资源。没有#threads vs#processor的中心协调。
良好的hygeine 要求一个库,无论是在客户端应用程序还是服务器应用程序中使用,都使用公共线程池,这意味着QUWI。
最后要意识到的是这个;
托管线程是后台线程或前台线程。后台线程与前台线程相同,但有一个例外:后台线程不会使托管执行环境保持运行。一旦所有前台线程在托管进程中停止(其中.exe文件是托管程序集),系统将停止所有后台线程并关闭。
属于托管线程池的线程(即IsThreadPoolThread属性为true的线程)是后台线程。从非托管代码进入托管执行环境的所有线程都标记为后台线程。通过创建和启动新Thread对象生成的所有线程都是默认的前台线程。
如果使用线程来监视活动(例如套接字连接),请将其IsBackground属性设置为true,以便线程不会阻止进程终止。
来自MSDN site。
答案 2 :(得分:4)
答案 3 :(得分:2)
如果您的任务很短,您很可能会通过QueueUserWorkItem或BeginInvoke在线程池上调度任务来获得更好的性能,因为您可以避免重复创建和销毁自己的线程的成本。
线程池显然也支付创建线程的费用,但它重用了线程,因此您不需要支付每个任务的成本。
您可能还想查看Threading in C#(免费电子书)。
答案 4 :(得分:2)
这是使用ThreadPool.QueueUserWorkItem的另一个优点。
我有一个winforms应用程序,有一个听力。我最初使用
实现了这个 heartbeat = new Thread(HeartbeatDoWork);
heartbeat.Start();
98%的时间都可以正常工作。但是,当应用程序关闭时,它有时不会“正常”死亡,即在任务管理器中仍然可以看到该过程。
简而言之,心跳发布了一个事件,它正在处理事件(CAB pub / sub),它被“卡住”。
修复很简单,只需更改为使用此
ThreadPool.QueueUserWorkItem(HeartbeatDoWork);
我确信在旋转我自己的线程时我可以做一些额外的事情,并且它会被正确清理,但这更简单,更快速,更容易理解...... ThreadPool可以完成所有工作我
答案 5 :(得分:1)
无论何种类型,线程池在.NET应用程序中始终可用。
通过ThreadPool.QueueUserWorkItem
从线程池启动线程的成本不会超过启动自己的线程的成本,而且可能会更少。
如果您只想在短时间内使用几个线程,那么请使用线程池。这就是它的用途。
答案 6 :(得分:0)
如果你正在创建自己的线程,那么在第一次使用时启动它们是有意义的,然后让它们暂停一段时间以防你再次需要它们 - 你让一个空闲的线程等待一个事件,这样你就可以重新激活通过发信号通知事件,经过足够长的时间后,线程将自行唤醒并退出,回收未使用的资源。通过保持少量空闲线程可以重用,可以减少不断创建和销毁线程的开销(这对于足够短暂的操作来说非常重要)。
但这基本上是线程队列已经为你做的 - 所以你也可以使用它。这是一个解决的问题。
类似的争论曾经发生在C ++社区关于字符串,信不信由你。我们每个人都应该编写自己的字符串类,还是应该使用std::string
?答案取决于你是想学习如何编写一个字符串类,或者你已经完成了这个,并且你想继续发明一些新东西。
答案 7 :(得分:0)
使用Thread.Start()
的一个好处是,您以后可以故意中止该主题。在ThreadPool
中,您在排队后无法控制线程执行。
答案 8 :(得分:0)
我的大部分阅读都与Marcel一致,如果事情发生,控制线程的能力(Abort ......)应该被认真考虑,特别是如果你正在处理你无法控制的第三方电话并可能挂起