基于pthread的C语言生产者/消费者实现:消费者循环阻塞问题
解决Pthread生产者/消费者模型中消费者循环阻塞的问题
你提到用全局变量num_available作为条件变量fill和empty的状态,现在消费者出现循环阻塞的情况——虽然你贴的生产者代码没写完,但结合这类问题的常见坑点,我来帮你排查和修正:
最可能触发阻塞的几个原因
- 条件变量与互斥锁绑定错误:
pthread_cond_wait必须在持有互斥锁时调用,而且唤醒后必须重新检查条件(虚假唤醒是常态,不是例外) num_available的更新无保护:全局变量的增减不是原子操作,没加锁的话会出现竞态,导致消费者读到错误的状态一直等待- 唤醒时机或目标错误:生产者填充任务后没唤醒
fill条件变量,或者误唤醒了empty,消费者就会一直卡着
修正后的完整生产者代码示例
先假设你的全局同步原语和参数结构是这样的:
#include <pthread.h> // 全局状态与缓冲区配置 #define MAX_BUFFER 10 int num_available = 0; char buffer[MAX_BUFFER]; // 假设用这个缓冲区存任务数据 // 同步工具 pthread_mutex_t task_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_fill = PTHREAD_COND_INITIALIZER; // 有任务可消费时唤醒 pthread_cond_t cond_empty = PTHREAD_COND_INITIALIZER; // 缓冲区有空位时唤醒 // 生产者参数结构体 typedef struct { char *data; size_t filesize; } producer_arg_struct;
下面是修复后的生产者逻辑:
void *producer(void *arg){ producer_arg_struct *real_arg = (producer_arg_struct*) arg; char *data = real_arg->data; size_t filesize = real_arg->filesize; size_t cur_pos = 0; // 跟踪当前处理到的数据位置 while(cur_pos < filesize) { // 第一步:加锁进入临界区 pthread_mutex_lock(&task_mutex); // 等待缓冲区有空位(用while循环,防止虚假唤醒) while(num_available >= MAX_BUFFER) { pthread_cond_wait(&cond_empty, &task_mutex); } // 填充任务到缓冲区(这里按字节示例,你可以改成自己的 chunk 逻辑) buffer[num_available] = data[cur_pos]; cur_pos++; num_available++; // 唤醒等待的消费者:现在有新任务了 pthread_cond_signal(&cond_fill); // 解锁退出临界区 pthread_mutex_unlock(&task_mutex); } // 生产者完成所有任务后,要通知消费者退出 pthread_mutex_lock(&task_mutex); // 全局标志:生产者已完成所有任务 extern int producer_finished; producer_finished = 1; pthread_cond_broadcast(&cond_fill); // 广播唤醒所有消费者,避免漏唤醒 pthread_mutex_unlock(&task_mutex); pthread_exit(NULL); }
消费者端的配套修正
消费者也要对应调整,确保逻辑正确:
// 全局标志,生产者结束时设为1 int producer_finished = 0; void *consumer(void *arg){ while(1) { pthread_mutex_lock(&task_mutex); // 等待有任务,同时检查生产者是否已经结束 while(num_available == 0 && !producer_finished) { pthread_cond_wait(&cond_fill, &task_mutex); } // 如果生产者结束且没有剩余任务,退出循环 if(num_available == 0 && producer_finished) { pthread_mutex_unlock(&task_mutex); break; } // 消费任务(示例:取出缓冲区数据) char task_data = buffer[num_available - 1]; num_available--; // 唤醒生产者:现在缓冲区有空位了 pthread_cond_signal(&cond_empty); pthread_mutex_unlock(&task_mutex); // 这里写你的任务处理逻辑 // process_task(task_data); } pthread_exit(NULL); }
核心注意事项
- 永远用while检查等待条件:不要用if,因为虚假唤醒会让线程在条件不满足时醒来
- 临界区必须加锁:所有访问
num_available和缓冲区的操作都要在互斥锁保护下 - 唤醒正确的条件变量:生产者填完唤醒
cond_fill,消费者消费完唤醒cond_empty - 生产者结束要广播:用
pthread_cond_broadcast而不是signal,确保所有消费者都收到结束信号
内容的提问来源于stack exchange,提问作者Jay Wang
相关产品推荐
相关产品推荐

