我在许多地方都读过.net Threadpool用于短时间跨度任务(可能不超过3秒)。在所有这些提及中,我没有找到为什么不应该使用它的具体原因。
即使有些人说如果我们长期使用任务并导致死锁,也会导致令人讨厌的结果。
有人可以用简单的英语用技术理由解释为什么我们不应该使用线程池进行长时间任务?
具体来说,我甚至想提供一个场景,并想知道为什么不应该在这种情况下使用ThreadPool,并且背后有正当理由。
场景:我需要处理数千个用户数据。用户的处理数据从本地数据库中检索,并使用我需要连接到其他位置托管的API的信息,API处理后的响应将存储在本地数据库中。
如果我使用线程限制为20的ThreadPool,有人可以在这种情况下解释我的陷阱吗?每个用户的处理时间可以从3秒到1分钟(或更长)。
答案 0 :(得分:16)
线程池的目的是避免创建线程所花费的时间比使用花费的时间长的情况。通过重用现有线程,我们可以避免这种开销。
缺点是线程池是一个共享资源:如果你正在使用一个线程,那么其他东西就不能。因此,如果您有批次长时间运行的任务,最终可能导致线程池饥饿,甚至可能导致死锁。
不要忘记您的应用程序代码可能不是使用线程池的唯一代码......系统代码也使用它很多。
听起来你可能希望拥有自己的生产者/消费者队列,只有少量线程处理它。或者,如果您可以使用异步API与其他服务进行通信,您可能会发现您的计算机上的每一处处理都是短暂的。
答案 1 :(得分:2)
它与线程池调度程序的工作方式有关。它努力确保它不会释放比CPU内核更多的等待线程。这是一个好主意,运行更多线程而不是核心是浪费,因为Windows花费时间在线程之间切换上下文。缩短完成工作所需的总时间。
一旦TP线程完成,就允许另一个线程运行。每秒两次,TP调度程序在运行的线程未完成时进入。它无法分辨为什么这些线程花了这么多时间来完成他们的工作。半秒是一个很多的CPU周期,大约十亿左右。因此,它假定线程正在阻塞,等待某种I / O完成。像dbase查询,磁盘读取,套接字连接尝试,类似的东西。
它允许另一个线程运行。你现在有更多的线程,然后你有核心。如果那些原始线程确实阻塞,那么这不是一个真正的问题,它们不会消耗任何CPU周期。
你可以看到这导致了什么:如果你的线程运行3秒,那么它会创建一些logjam。它会延迟,但不会阻止正在等待运行的其他TP线程。如果你的线程需要花费这么多时间,因为它经常阻塞,那么你最好创建一个常规的Thread。如果您真的关心线程不会被TP调度程序延迟,那么您也应该使用线程。
TP调度程序在.NET 4.0中被修改了btw,我写的内容对于早期版本来说才是真的。基础知识仍然存在,它只是使用更智能的调度算法。基于反馈,通过测量吞吐量动态调度。这非常重要,如果你有一个很多的TP线程。
答案 2 :(得分:2)
没有真正触及的两个原因:
通常,让线程池线程获取锁(必要时等待)不是问题。如果一个线程池线程需要等待另一个线程池线程释放锁,那么后一个线程首先获得锁的事实意味着它已经启动了。另一方面,等待例如如果使用I / O回调例程来标记数据的到达,则从连接到达的某些数据可能会导致死锁。如果有太多的线程池线程正在等待I / O回调以指示数据已到达,则系统可能会决定推迟回调,直到其中一个线程池线程完成。