我很想知道在给定以下场景/约束的情况下是否有一个广泛接受的解决方案来管理线程池中的线程资源:
我的问题是关于线程池实现的理论。可以使用什么算法有效地将可用线程分配给所有存储桶中的传入作业?
编辑:假设有可用的空闲线程,另一个设计目标是尽可能消除入队作业和拾取处理作业之间的延迟。
Edit2 :在我认为有相对大量的队列(50-100)具有不可预测的活动水平的情况下,但可能只有25%的队列将处于活动状态在任何给定的时间。
我能想到的第一个(也是最昂贵的)解决方案是简单地为每个队列分配一个线程。虽然这将确保立即获取传入请求,但这显然是低效的。
第二种解决方案是根据预期的活动级别将队列组合在一起,以便队列数与池中的线程数一致,允许将一个线程分配给每个队列。这里的问题是传入的作业,否则可以并行处理,将被迫相互等待。
第三种解决方案是创建最大队列数,每个队列对应一组必须连续处理的作业,但只根据我们期望在任何给定时间忙的队列数分配线程(也可能在运行时由池调整)。所以这就是我的问题所在:假设我们有比队列更多的队列,那么池如何以最有效的方式将空闲线程分配给传入的作业呢?
我想知道是否有一种被广泛接受的方法。或者,如果有不同的方法 - 谁使用哪一个?有哪些优点/缺点等?
Edit3 :这可能最好用伪代码表示。
答案 0 :(得分:2)
你应该消除nr。 2来自您的规格。您真正需要遵守的是线程占用存储桶并按顺序处理存储桶内的队列。使用另一个线程池处理序列化队列或并行执行某些任务序列化是没有意义的。因此,您的规范只会变成线程迭代桶中的fifo,并由poolmanager来插入正确构造的桶。所以你的桶将是:
struct task_bucket
{
void *ctx; // context relevant data
fifo_t *queue; // your fifo
};
然后由您决定使线程池足够智能,以便知道在队列的每次迭代中要做什么。例如,ctx可以是一个函数指针,队列可以包含该函数的数据,因此工作线程只需使用提供的数据在每次迭代时调用该函数。
反映意见: 如果存储桶列表的大小是事先已知的并且在程序的生命周期内不太可能发生变化,那么您需要确定这对您来说是否重要。您需要某种方式让线程选择要采用的存储桶。最简单的方法是让一个FIFO队列由管理器填充并由线程清空。经典读者/作家。
另一种可能性是堆。 worker从堆中删除最高优先级并处理存储桶队列。工作人员的删除和经理的插入都会重新排序堆,以便根节点具有最高优先级。
这两种策略都假设工人扔掉桶子,而经理就会制造新桶子。
如果保持存储桶很重要,则存在工作人员仅参与上次修改的任务的风险,因此管理员将需要重新排序存储桶列表或修改每个存储桶的优先级,并且工作人员迭代寻找最高优先级。重要的是,当线程正在工作时,ctx的内存仍然相关,或者线程也必须复制它。工作人员可以简单地在本地分配队列,并在队列中将队列设置为NULL。
答案 1 :(得分:2)
补充:我现在倾向于同意你可以开始简单,只为每个存储桶保留一个单独的线程,只有当这个简单的解决方案被理解为存在问题时,你才会寻找不同的东西。更好的解决方案可能取决于简单问题究竟出现的问题。
无论如何,我在下面留下我的初步答案,附加一个事后的想法。
您可以创建一个特殊的全局队列“作业在桶X中可用”信号。
所有空闲工作人员都会在此队列中等待,当一个信号被放入队列时,一个线程将接受它并继续到相应的存储桶来处理那里的工作,直到存储桶变空。
当传入作业提交到有序存储桶时,应检查是否已将工作线程分配给此存储桶。如果已分配,则新作业最终将由此工作线程处理,因此不应发送任何信号。如果未分配工作人员,请检查存储桶是否为空。如果为空,则将信号放入全局信号队列中,以便新作业到达此存储桶;如果不是空的,那么这样的信号应该已经发出并且工作线程应该很快到达,所以什么都不做。
补充:我认为如果线程数少于“活动”桶的数量并且有一个非结束的流量,我上面的想法会导致一些工作的饥饿传入的任务。如果所有线程都已忙,并且新作业到达尚未提供的存储桶,则可能需要很长时间才能释放线程以处理此新作业。因此需要检查是否存在空闲工作,如果没有,则创建一个新工作...这会增加复杂性。
答案 2 :(得分:0)
保持简单:我每个队列使用1个线程。简单是值得的,线程非常便宜。在大多数操作系统上,100个线程不会成为问题。
通过每个队列使用一个线程,您还可以获得真正的调度程序。如果一个线程阻塞(取决于你正在做什么),另一个线程可以排队。在每一个阻塞之前,你不会陷入僵局。如果您使用较少的线程,则不能说同样的情况 - 如果线程恰好是服务块的队列,那么即使其他队列是“可运行的”,即使这些其他队列可能取消阻塞被阻塞的线程,你会陷入僵局。
现在,在特定情况下,使用线程池可能是值得的。但接下来你谈的是优化特定系统,细节很重要。线程有多贵?调度程序有多好?阻塞怎么样?队列有多长,更新频率等等。
所以一般来说,只有你有大约100个队列的信息,我只需要为每个队列寻找一个线程。是的,有一些开销:所有解决方案都有。线程池将引入同步问题和开销。并且有限数量的线程的开销相当小。你主要谈论的是大约100MB的地址空间 - 不一定是内存。如果您知道大多数队列将处于空闲状态,您可以进一步实施优化以停止空队列上的线程并在需要时启动它们(但要注意竞争条件和颠簸)。