什么时候不应该在 .Net 中使用 ThreadPool? [关闭]

Posted

技术标签:

【中文标题】什么时候不应该在 .Net 中使用 ThreadPool? [关闭]【英文标题】:When should I not use the ThreadPool in .Net? [closed] 【发布时间】:2010-09-05 20:00:05 【问题描述】:

我什么时候应该在 .Net 中使用 ThreadPool?

看起来最好的选择是使用线程池,在这种情况下,为什么它不是唯一的选择?

您对此有何经验?

【问题讨论】:

【参考方案1】:

@Eric,我不得不同意 Dean 的看法。线程很昂贵。您不能假设您的程序是唯一运行的程序。当每个人都贪图资源时,问题就会成倍增加。

我更喜欢手动创建线程并自己控制它们。它使代码非常容易理解。

在适当的时候很好。但是,如果您需要一堆工作线程,那么您所做的就是让您的代码更加复杂。现在您必须编写代码来管理它们。如果您只是使用线程池,您将免费获得所有线程管理。并且该语言提供的线程池很可能比您自己创建的任何东西都更健壮、更高效且错误更少。

Thread t = new Thread(new ThreadStart(DoSomething));  
t.Start();  
t.Join();  

我希望您通常在Start()Join() 之间有一些额外的代码。否则,多余的线程是无用的,无端浪费资源。

人们太害怕线程使用的资源了。我从未见过创建和启动线程花费超过一毫秒。您可以创建的线程数没有硬性限制。 RAM 使用率极低。一旦你有几百个线程,CPU 就会因为上下文切换而成为一个问题,所以你可能想要对你的设计有所了解。

在现代硬件上,一毫秒是 很长 的时间。这是 3GHz 机器上的 300 万个周期。再说一次,你不是唯一一个创建线程的人。您的线程与所有其他程序的线程一起竞争 CPU。如果您使用的线程不是太多,另一个程序也是如此,那么您一起使用了太多的线程。

说真的,不要让生活变得过于复杂。除非您需要它提供的非常具体的东西,否则不要使用线程池。

确实如此。不要让生活变得更复杂。如果您的程序需要多个工作线程,请不要重新发明***。使用线程池。这就是它在那里的原因。你会推出自己的字符串类吗?

【讨论】:

有些人在一个字符数组上滚动他们自己的包装器,然后像字符串一样使用它......所以它是可能的......这也很可悲。 我投了反对票。这不是答案;这是对别人的回复。 这个答案已经足够好了。但是此刻有 22 个赞成票?它不是好。雷德蒙德没有大量的选民被淘汰,是吗? @Will,这是 4 年前的评论。它平均每月不到一半的赞成票。几乎没有令人难以置信的投票水平。 @hwiechers,一半是回答,一半是回答。所以发布时没有 cmets,我不愿意浪费时间浏览我所有的旧答案并将它们转换为 cmets。【参考方案2】:

我不使用ThreadPool 进行廉价多线程的唯一原因是如果我需要……

    与正在运行的方法交互(例如,杀死它) 在STA thread 上运行代码(这发生在我身上) 在我的应用程序死亡后保持线程处于活动状态(ThreadPool 线程是后台线程) 如果我需要更改线程的优先级。 ThreadPool 中线程的优先级不能改变,默认为 Normal。

PS: MSDN 文章"The Managed Thread Pool" 包含一个标题为“何时不使用线程池线程”的部分,其中包含一个非常相似但稍微更完整的列表不使用线程池的可能原因。

您需要跳过ThreadPool 的原因有很多,但如果您不知道这些原因,那么ThreadPool 对您来说应该已经足够好了。

或者,看看新的Parallel Extensions Framework,里面有一些简洁的东西可以满足你的需要,而不必使用ThreadPool

【讨论】:

你说:- 在我的应用程序死亡后保持线程处于活动状态(ThreadPool 线程是后台线程),但我知道后台线程总是以主线程结束。那你说呢?? @Mou:很遗憾,我不知道你想说什么。 如果您正在阅读本文,那么您现在应该使用任务而不是线程池。 你说keep the thread alive after my application has died (ThreadPool threads are background threads)...我说得对。是真的吗?如果线程池线程是后台线程,那么线程可以在主线程死亡时运行吗?请解释一下 我遇到了问题 #1(将 CancellationToken 支持添加到第三方库中长时间运行(5 分钟以上)的“纯”(足够)函数调用)和 #2(并发WPF ImageSource 渲染...每个并发渲染需要在同一个 STA 线程上开始和结束,但可以在同一个应用程序中冻结以从其他线程显示),所以这对我来说是正确的答案。大多数情况下,托管线程池(或最终使用它的无数其他事物之一,例如 TPL)是正确的,但有时正确的答案确实是启动一个新线程并继续你的生活。【参考方案3】:

只要您有工作线程的概念,线程池就很有意义。任何时候您都可以轻松地将处理划分为较小的作业,每个作业都可以独立处理,工作线程(因此是线程池)是有意义的。

当您需要执行完全不同且不相关的操作的线程时,线程池没有意义,这些操作不能被视为“作业”;例如,一个线程用于 GUI 事件处理,另一个用于后端处理。当处理形成管道时,线程池也没有意义。

基本上,如果您有启动、处理作业和退出的线程,那么线程池可能是要走的路。否则,线程池将无济于事。

【讨论】:

你能解释一下“处理形成管道时线程池没有意义”这句话吗?假设我有需要压缩然后加密的工作项,并且压缩使用 10 倍的计算作为加密。为什么不使用压缩线程与加密线程比率为 10:1 的线程池? 线程池通常用于当程序有独立的、离散的工作要做时。如果工作线程之间存在通信(例如在管道中),那么您实际上并没有线程池场景。【参考方案4】:

对于 quarrelsome 的回答,我要补充一点,如果您需要保证您的线程将立即开始工作,最好不要使用 ThreadPool 线程。每个应用程序域的最大运行线程池线程数是有限的,因此如果它们都忙,您的工作可能不得不等待。毕竟它被称为“队列用户工作项”。

当然有两个警告:

    您可以在运行时更改代码中线程池线程的最大数量,因此没有什么可以阻止您检查当前与最大数量并在需要时提高最大值。 启动新线程会带来时间上的损失 - 您是否值得接受打击取决于您的具体情况。

【讨论】:

这是正确答案。当操作系统中的爬山算法决定创建一个新线程以清除不断增加的项目队列会使您震惊时,我看到长时间延迟的次数(我之前看到过超过 4 秒的延迟)。 MS 非常清楚地表明,线程池适用于不长时间运行的后台任务,实际上它们有一个名为“LongRunning”的标志,您可以通过线程池类为线程设置该标志,该线程池类实际上在托管池之外创建该线程,例如出于这个原因,一个新的单独线程和不受管理的线程。【参考方案5】:

我不是作为一个只有 理论知识在这里。我写 并保持大量应用 大量使用多线程, 而且我通常找不到线程 池是正确的答案。

啊,来自权威的争论 - 但请始终留意可能在 Windows 内核团队中的人。

我们都没有争论这样一个事实,即如果您有一些特定的要求,那么 .NET 线程池可能不是正确的选择。我们反对的是对机器创建线程的成本微不足道。

首先在 ThreadPool 的存在理由中创建线程的巨大开销。我不希望我的机器被那些被错误地告知创建线程的费用的人编写的代码填充,例如,不知道它会导致在每个单独的 DLL 中调用一个方法附加到进程(其中一些将由第 3 方创建),并且很可能会引发大量代码,这些代码根本不需要在 RAM 中,而且几乎可以肯定不需要在 L1 中。

现代机器中内存层次结构的形状意味着“分散” CPU 的注意力是您可能做的最糟糕的事情,每个关心自己工艺的人都应该努力避免它。

【讨论】:

【参考方案6】:

当您要执行一个需要很长时间的操作时,或者可能是一个连续的后台线程。 我想您总是可以增加池中可用的线程数量,但不会产生永远不会返回池的线程的管理成本。

【讨论】:

【参考方案7】:

MSDN 在这里列出了一些原因:

http://msdn.microsoft.com/en-us/library/0ka9477y.aspx

有几种情况适合创建和 管理自己的线程,而不是使用线程池线程:

您需要一个前台线程。 您需要线程具有特定的优先级。 您的任务会导致线程长时间阻塞。线程池有最大线程数,所以大 阻塞的线程池线程数可能会阻止任务 开始。 您需要将线程放入单线程单元中。所有 ThreadPool 线程都在多线程单元中。 您需要有一个与线程相关联的稳定身份,或将线程专用于某项任务。

【讨论】:

【参考方案8】:

线程池线程适用于满足以下两个条件的任务:

    该任务无需花费大量时间等待某事发生 任何正在等待任务完成的事情都可能在等待许多任务完成,因此它的调度优先级不会对事情产生太大影响。

使用线程池线程而不是创建新线程将节省大量但有限的时间。如果该时间与执行任务所需的时间相比很重要,那么线程池任务可能是合适的。但是,执行任务所需的时间越长,使用线程池的好处就越小,任务阻碍线程池效率的可能性就越大。

【讨论】:

【参考方案9】:

@埃里克

@Derek,我不完全同意您用作示例的场景。如果您不确切知道您的机器上正在运行什么,以及您的应用在一定负载下将使用多少总线程、句柄、CPU 时间、RAM 等,那么您就有麻烦了。

您是您编写的程序的唯一目标客户吗?如果不是,您无法确定其中的大部分内容。当您编写程序时,您通常不知道它是否会有效地单独执行,或者它是否会在受到 DDOS 攻击的网络服务器上运行。您无法知道您将拥有多少 CPU 时间。

假设您的程序的行为会根据输入而发生变化,甚至很难确切地知道您的程序将消耗多少内存或 CPU 时间。当然,您应该对程序的运行方式有一个很好的了解,但是大多数程序从不分析以确定将使用多少内存、多少句柄等,因为全面分析的成本很高。如果您不编写实时软件,那么付出的努力是不值得的。

一般来说,声称知道您的程序将如何运行是牵强附会的,而声称知道有关机器的一切则很可笑。

老实说,如果您不确切知道应该使用什么方法:手动线程、线程池、委托,以及如何实现它来满足您的应用程序的需要,那么您就有麻烦了。

我并不完全不同意,但我真的不明白这有什么关系。这个网站专门在这里,因为程序员并不总是有所有的答案。

如果您的应用程序足够复杂,需要限制您使用的线程数量,您是不是几乎总是需要比框架提供的更多控制权?

没有。如果我需要一个线程池,我将使用提供的线程池,除非并且直到我发现它还不够。我不会简单地假设提供的线程池不足以满足我的需求,而不确认是这种情况。

我在这里不是作为一个只有理论知识的人说话。我编写和维护大量使用多线程的大量应用程序,但我通常不认为线程池是正确的答案。

我的大部分专业经验都与多线程和多处理程序有关。我也经常需要推出自己的解决方案。这并不意味着线程池在许多情况下没有用或不合适。线程池是为处理工作线程而构建的。在适合多个工作线程的情况下,提供的线程池通常应该是第一种方法。

【讨论】:

以上是关于什么时候不应该在 .Net 中使用 ThreadPool? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

NET牛人应该知道些什么?

什么时候应该在 ASP.NET MVC 中使用异步控制器?

什么时候不应该使用 Web 服务?

什么时候应该在 WPF 中使用依赖属性?

什么时候应该在 WPF 中使用依赖属性?

什么时候应该使用标准的 html 标签/输入,什么时候应该使用 asp.net 控件?