为啥线程没有在线程池中重用?

Posted

技术标签:

【中文标题】为啥线程没有在线程池中重用?【英文标题】:Why the threads are not reused in the Thread Pool?为什么线程没有在线程池中重用? 【发布时间】:2022-01-10 18:42:16 【问题描述】:

我正在尝试在 C 中实现线程池模型,但线程本身存在问题。我创建了 10 个 pthread,并且我有一个包含它们需要处理的数据包的队列,但问题是,例如,当我有 100 个数据包时,线程仍然只处理前 10 个。所以,我猜他们只是在第一次使用后停止工作。处理完前一个数据包后,如何让线程重用?

这里是线程的创建:

for(i=0;i<10;i++)
        pthread_create(&t[i],NULL,func,args);
    

然后,当我收到要使用的新数据包时,我会将其排入工作队列:

pthread_mutex_lock(&q_mux);
enqueue(queue,p);
pthread_cond_broadcast(&qcond);
pthread_mutex_unlock(&q_mux);

最后,我将队列中的第一个数据包取出来:

pthread_mutex_lock(&q_mux);
while(isempty(queue))  
        pthread_cond_wait(&queue_cond,&q_mux);

p=queue->head->p;
dequeue(queue);
pthread_mutex_unlock(&q_mux);
process(p);

【问题讨论】:

是的,您在问为什么有些代码不起作用,但您没有显示任何代码。见minimal reproducible example。 @qwerty 发布了足够多的代码,以便有人可以将代码复制并粘贴到文本编辑器中,然后编译并运行它以查看问题是否发生。 好吧,'while(1) processQueueItem();'似乎是个不错的计划。 【参考方案1】:

假设:线程处理逻辑周围不存在“直到队列空循环”,线程在等待和处理单个项目后正常终止。

这与仅处理 10 个项目的行为相匹配,创建的 10 个线程中的每个线程都有 1 个项目。 pthread_create 只是启动一个新线程,线程在处理完成后不会自动重新启动。

考虑:

 // Loop until all items processed..
 while (!shutdown) 
     // take first packet and dequeue.. 99 packets of bits in the queue..
     pthread_mutex_lock(&q_mux);
     // ..
 

当然,必须按照shutdown 的含义工作,并将其与(not) isempty 检查结合使用,以避免空队列出现死锁。

处理这种情况的一种方法是在所有要处理的项目之后排队一个“已完成”项目。当一个线程遇到这个项目时,它们将从每个线程内的主循环返回而终止。

// Inside a thread, loop indefinitely and consume from the
// queue until encountering an item that indicates processing
// should stop.
while (true) 
    pthread_mutex_lock(&q_mux);
    while (!shutdown & isempty(queue))   
        pthread_cond_wait(&queue_cond,&q_mux);
    
    p=queue->head->p;
    if (p->is_finished) 
        // Run into 101'th item with special flag
        pthread_mutex_unlock(&q_mux);
        // signal other waiting threads (allows replacing broadcast with signal)
        pthread_cond_signal(&qcond);
        break;
       
    dequeue(queue);
    pthread_mutex_unlock(&q_mux);
    process(p);

在这种情况下,“已完成”项目不会出队,因此所有线程都可以看到它;另一种变体是为每个线程添加一个“已完成”项并允许它出列,这样当所有线程终止时队列完全耗尽。

另一种方法是对队列本身使用“添加完成”标志,并确保它也向互斥体发出信号。 (当队列被标记为“添加完成”时,它不能添加新项目。)

while (isempty(queue)) 
    if (isaddingcomplete(queue)) 
        // empty and nothing else can be added..
        pthread_mutex_unlock(&q_mux);
        // signal other waiting threads (allows replacing broadcast with signal)
        pthread_cond_signal(&qcond);
        return;
    
    pthread_cond_wait(&queue_cond,&q_mux);

给地鼠换皮的方法有很多。

【讨论】:

我是线程新手,但我认为线程池模型的主要原理是线程在完成任务后只会等待,当队列中有新数据包时,它会自动占用它。或者换句话说,线程在空闲时,总是试图从队列中获取一个新的数据包(并且当队列为空时才停止)。 @qwerty 线程从它们的身体返回时完成。没有任何标准会“重新运行”线程。虽然这似乎是线程池在特定队列处理上的一个概念的实现,但 pthreads_create 与线程池没有任何隐含关系,它只执行所要求的操作:它创建并启动一个新的线程。 @qwerty, Re, “我认为...[the] 线程...只会...”您创建的线程永远不会做任何事情,除非执行 为其提供的代码。如果您提供的代码从队列中获取 one 对象并“处理”它,那么这就是线程将执行的操作。这就是线程将做的所有。如果您希望线程“等待 [直到] 队列中有一个新数据包......”,那么由您编写等待和处理其他数据包的代码。

以上是关于为啥线程没有在线程池中重用?的主要内容,如果未能解决你的问题,请参考以下文章

线程池

Android中的线程池

五种线程池的分类和作用

五种线程池的分类和作用

java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?

为啥线程池定时的任务在熄屏下不能执行