线程与线程池


137

使用新线程和使用线程池中的线程有什么区别?有哪些性能优势?为什么我应该考虑使用池中的一个线程而不是我明确创建的线程?我在这里专门考虑.NET,但一般示例都可以。

Answers:


110

线程池将为频繁且相对短的操作提供以下好处:

  • 重用已经创建的线程,而不是创建新的线程(昂贵的过程)
  • 当对新工作项的请求激增时,限制线程创建的速度(我相信这仅在.NET 3.5中)

    • 如果将100个线程池任务排队,则它将仅使用已创建的线程数来满足这些请求(例如10个)。线程池将进行频繁检查(我相信3.5 SP1中每500毫秒一次),如果有排队的任务,它将创建一个新线程。如果您的任务很快,那么新线程的数量将会减少,而将10个左右的线程重新用于短期任务将比在先创建100个线程更快。

    • 如果您的工作负载始终有大量线程池请求进入,则线程池将通过上述过程在池中创建更多线程,从而根据您的工作负载进行调整,以使有更多线程可用于处理请求

    • 检查这里的更深入的信息如何在引擎盖下的线程池功能

如果作业需要相对较长的时间(大概一两秒钟左右,但这取决于具体情况),那么自己创建一个新线程会更合适。

@Krzysztof-线程池线程是后台线程,它将在主线程结束时停止。手动创建的线程默认情况下是前台的(在主线程结束后将继续运行),但是可以在调用它们之前将其设置为后台。


5
我唯一想知道的是来自MSDN(msdn.microsoft.com/en-us/library/1c9txz50.aspx)的以下语句“仅当执行的前台线程数小于处理器数量时,才会执行后台线程”。那么这是否意味着在内核之间进行分配工作时,前台线程将获得优先权?
— cdiggins 2010年

1
➤您不能中止或中断线程池中的线程。➤您无法从线程池中加入线程。为此,您必须使用其他一些机制
— Zinov

14

.NET托管线程池:-

  • 根据当前的工作量和可用的硬件自行调整大小
  • 包含工作线程和完成端口线程(专门用于服务IO)
  • 针对大量相对短暂的操作进行了优化

存在其他可能更适合长时间运行的操作的线程池实现。

具体来说,请使用线程池来防止您的应用创建太多线程。线程池的最重要特征是工作队列。也就是说,一旦您的计算机足够繁忙,线程池就会将请求排队,而不是立即产生更多线程。

因此,如果您要创建数量很少的线程,请自己创建它们。如果您无法预先确定可以创建多少个线程(例如,它们是为响应传入的IO而创建的),并且它们的工作将很短,请使用线程池。如果您不知道有多少,但是他们的工作将长期运行,则平台中没有任何帮助您的信息-但您可能能够找到合适的替代线程池实现。


在.NET中,可以在没有线程池的情况下使用完成端口吗?我假设异步I / O方法是唯一方法(在.NET中),并且它们使用线程池
— Karg,

10

也

new Thread().Start()

如果关闭程序,将生成不会死亡的前台线程。ThreadPool线程是后台线程,在您关闭应用程序时会死掉。


11
您始终可以将线程设置为后台。默认情况下,它们只是前景。
— 克里斯·埃里克森

3
nemo:var t = new Thread(...); t.BackgroundThread = true; t.Start();
— 里卡多·爱茉莉2009年

18
关于术语“程序”的澄清。桌面应用程序在一个进程中运行,并且至少具有一个管理UI的前台线程。只要具有前台线程,该进程将继续运行。关闭桌面应用程序时,前台UI线程会停止,但是如果它具有其他前台线程,则不一定会停止该进程。
— G-Wiz 2010年

8

我对这些资源的相对使用量感到好奇,并在Windows 8上使用.net 4.0版本在我的2012年双核Intel i5笔记本电脑上运行了一个基准测试,线程池平均花费0.035毫秒开始,而线程平均花费5.06。多发性硬化症。换句话说,对于大量短寿命线程,池中的线程启动速度大约快300倍。至少在测试范围(100-2000)线程中,每个线程的总时间似乎相当恒定。

这是基准测试的代码:

    for (int i = 0; i < ThreadCount; i++) {
        Task.Run(() => { });
    }

    for (int i = 0; i < ThreadCount; i++) {
        var t = new Thread(() => { });
        t.Start();
    }

在此处输入图片说明


5
我认为这是因为ThreadPool重用了创建的线程,而不是创建新的线程(这非常昂贵)
— fabriciorissetto


1

对于线程池,线程本地存储不是一个好主意。它赋予线程“身份”;并非所有线程都相等。现在,如果您只需要一堆相同的线程,并且可以在不增加创建开销的情况下完成工作,那么线程池就特别有用。


1

如果需要大量线程,则可能要使用ThreadPool。他们重复使用线程,从而节省了线程创建的开销。

如果您只需要一个线程来完成某件事,则Thread可能是最简单的。


1

theadpool线程的主要需求是处理短期的小任务,这些小任务有望几乎立即完成。硬件中断处理程序通常在不适合非内核代码的堆栈上下文中运行,但是硬件中断处理程序可能会发现应该尽快运行用户模式I / O完成回调。为了运行这样的事情而创建新线程将是极大的过大杀伤力。有一些预先创建的线程可以分派来运行I / O完成回调或其他类似的东西,效率更高。

此类线程的一个关键方面是,如果I / O完成方法总是始终立即完成并且永不阻塞,并且当前正在运行此类方法的此类线程的数量至少等于处理器的数量,那么任何其他线程都是唯一的方法如果其他方法之一阻塞或它的执行时间超过了正常的线程时间切片,则可能在上述方法之一完成之前运行;如果按预期使用线程池,则这两种方法都不应该经常发生。

如果不能期望某个方法在开始执行后的100ms左右内退出,则应通过除主线程池之外的其他方法执行该方法。如果要执行的任务很多且占用大量CPU,但不会阻塞,则使用与“主”线程池分开的应用程序线程池(每个CPU内核一个)来分派它们可能会有所帮助。当运行无阻塞的CPU密集型任务时,线程多于内核会适得其反。但是,如果某个方法执行需要一秒钟或更长时间,并且大部分时间都处于阻塞状态,则该方法可能应该在专用线程中运行,并且几乎可以肯定不应该在主线程池线程中运行。如果需要通过I / O回调之类的东西触发长时间运行的操作,


0

通常,(我从未使用过.NET)线程池将用于资源管理。它允许将约束配置到您的软件中。由于性能原因也可以这样做,因为创建新线程可能会很昂贵。

可能还有系统特定的原因。在Java中(同样,我不知道这是否适用于.NET),线程管理器可能会在从池中拉出每个线程时应用特定于线程的变量,并在返回它们时取消设置它们(常见的传递类似身份)。

约束示例:我只有10个db连接,因此我只允许10个工作线程访问数据库。

这并不意味着您不应该创建自己的线程,但是在某些情况下使用池是有意义的。


0

如果您不知道或无法控制将创建多少个线程,则使用池是一个好主意。

只是使用表单在表单控件的位置更改事件上使用线程来更新数据库中的某些字段时出现问题(避免freez)。我的用户花了5分钟从数据库中获取一个错误(与Access的连接过多),因为他更改列表位置的速度太快了...

我知道还有其他方法可以解决基本问题(包括不使用访问权限),但是池化是一个好的开始。


0

线程:

  1. 创建线程比使用线程池要慢得多。
  2. 您可以更改线程的优先级。
  3. 与资源相关的进程中的最大线程数。
  4. 线程位于操作系统级别,并由操作系统控制。
  5. 当任务运行时间较长时,使用Thread是更好的选择

线程池:

  1. 在线程池上运行线程比直接创建线程要快得多。
  2. 您不能更改基于线程池运行的线程的优先级。
  3. 每个进程只有一个线程池。
  4. 线程池由CLR管理。
  5. 线程池对于短期操作很有用。
  6. 线程池中的线程数与应用程序负载有关。
  7. TPL任务基于线程池运行
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.