动态调整大小的线程池
Posted
技术标签:
【中文标题】动态调整大小的线程池【英文标题】:Dynamically re-sizable thread pool 【发布时间】:2012-08-06 17:34:36 【问题描述】:我的应用程序中有以下工作流程:可能有来自用户的 X 个请求(通常同时有 5-10 个)想要在系统中搜索某些内容(每个请求都在单独的线程中处理)。
每个搜索都可以并行处理(我目前正在实施)。线程/CPU 使用在这里并不是真正的问题,因为这些任务不是 CPU 密集型的。数据库是瓶颈。
目前我只为搜索机制设置了一个单独的数据库连接池 - 最大池大小设置为 10。我知道这并不多,但我不能将它设置得更高。现在我想弄清楚如何为每个搜索(每个用户)设置线程池。
每个请求(线程)都会产生一个单独的线程池(在这个池中每个线程将处理给定用户搜索的一部分)。如果例如 10 个用户一次点击“搜索”按钮,将这个线程池的大小设置为固定大小(假设为 4)真的会有问题,因为它会产生 10 个线程池,每个线程池有 4 个线程 = 40 个线程并且只有 10 个池中的数据库连接?我猜有些线程只是空闲,其余线程会竞相获得池的连接,但这真的会是个大问题吗?
如果是,那么最好的做法是什么:
-
在创建线程池时检查已有多少线程池
新的并相应地调整其(这个新池的)最大线程池大小(比如
已经有 2 个池,每个池有 4 个线程,那么新的池将
将最大线程设置为 2 甚至更新的池(假设只有 1 个最大线程)创建。这意味着下一个用户的搜索速度会大大降低。
创建具有相同最大线程池大小(即 4)的线程池,但实现我自己的线程池,它将动态检查应用程序中有多少线程并相应地调整 maxThreadPoolSize 大小(在这种情况下,例如所有线程池, 2 个旧的和新的,将缩小到 3 个线程)。这将要求每个线程池访问某个共享对象,其中包含有关应用程序中所有线程池的信息。
还有别的吗?
编辑: 感谢所有 cmets/答案。为了澄清为什么我想要每个请求都有一个线程池 - 这样做是为了让一个用户不会用完整个线程池。流程是这样的:当用户点击“搜索”时,会生成一个对象列表(这个列表可以从 1 个项目到数千个),然后为每个项目执行一个 DB 查找。现在这一切都是按顺序执行的。在我更改后,每个任务都会处理一次查找(因为在 DB 上的搜索速度非常慢,这给了我很大的提升 - 我知道我可以尝试做一些 DB 微调,但我不负责)。
问题是,如果我 User1 来并执行一个真正通用的搜索 X 千生成的项目可能需要几分钟(或更长时间)。因此,我可以在单个用户的 executor 中执行数千个任务。然后,如果我有一个共享线程池,可以说最多 10 个线程(与连接池的数量相同),这个请求将被放入线程池的队列中。现在,如果 User2 来执行他的搜索,他将不得不等待 User1 的搜索完成,因为他的搜索将被放入同一个队列中。这是我想通过每个请求的线程池来避免的情况。
我并不是真的那么害怕上下文切换,因为每次计算可能需要长达几秒钟的时间,所以它们不会经常发生。
目前我正在考虑一个共享线程池和一个管理器,每个用户线程会将其数据发送到该管理器,然后只要有空闲线程,该管理器就会将其发送到线程池。这样我就可以实现它(管理器)来发送来自不同用户的任务(即没有一个用户不会控制线程池)。
我看到这种方法的问题是我需要以某种方式通知“父线程”(即用户请求)它的所有任务都由经理处理并以某种方式将结果发送给它。
【问题讨论】:
为什么不使用单个共享线程池而不是 1/request? 您只执行搜索吗? @Matt Ball:这是一个很好的问题 :) 认为我有一个理由,但我需要重新考虑这一点。 如果每个请求已经有一个线程,则根本不需要额外的池。您需要一个用于 10 个并发数据库会话的连接池。 是的,但是如果有一个共享线程池,一个用户就不可能支配整个线程池吗? 【参考方案1】:现代处理器可以毫无问题地轻松处理数百个线程,但正如 @PeterLawrey 所暗示的那样,您的设计有些奇怪。如果如您所说,该操作在计算上并不昂贵,那么拥有非常多的线程将导致大量昂贵的上下文切换,从而导致性能下降。
额外的复杂性来自于您希望每个请求都有一个线程池,而连接池是每个应用程序:
如果每个请求都有一个线程池,则必须在每次收到新请求时创建并销毁它 无论您拥有多少万亿线程和预算为 100000 美元的超级计算机,执行有用工作的线程都不超过 10 个。您的直觉现在应该告诉您,问题在于您希望每个请求都有一个线程池,而理想的解决方案是在请求之间拥有一个共享线程池,线程数等于您的连接池大小。这将最大限度地提高线程重用率。
如果您还想避免单个请求占用您所有的计算能力,您可能需要添加一个层来决定谁有权安排额外的工作。使用您考虑的每个请求的线程池解决方案,您让调度程序为您执行此操作,这不是一个好主意,因为您无法控制算法。
相反,您可以实现自己的“公平算法”,例如通过 PriorityBlockingQueue 将块数较少的项目放在顶部,或者使用 ConcurrentHashMap 为每个用户存储要安排的作业列表和一个谁已经返回等等。
【讨论】:
感谢您的意见,但我对我在编辑中添加的共享线程池解决方案有些担忧。 谢谢 Edmondo,我也在考虑 ConcurrentHashMap 所以我想我会去的。以上是关于动态调整大小的线程池的主要内容,如果未能解决你的问题,请参考以下文章