在Linux多线程编程中,生产者消费者模型是一个经典且至关重要的同步问题。它完美地体现了多线程同步的核心思想,而Linux 阻塞队列则是实现该模型的高效数据结构。本文将围绕基于阻塞队列的生产消费者模型,从零开始详细讲解其原理、设计思路以及在Linux环境下的C语言实现,帮助初学者彻底掌握互斥锁、条件变量等关键技术。
生产者消费者模型描述了两个或多个线程(或进程)共享一个固定大小缓冲区的协作关系:生产者负责生成数据并放入缓冲区,消费者负责从缓冲区取出数据进行处理。当缓冲区满时,生产者必须等待;当缓冲区空时,消费者必须等待。这种模型可以有效解耦生产者和消费者,平衡处理速度差异,广泛应用于日志系统、任务队列、网络服务器等场景。
Linux 阻塞队列是一种线程安全的队列,它内部使用互斥锁保证数据一致性,并结合条件变量实现阻塞与唤醒机制。当队列满时,生产者线程会在“not full”条件变量上阻塞,直到消费者取出数据后发出唤醒信号;当队列空时,消费者线程会在“not empty”条件变量上阻塞,直到生产者放入数据后发出信号。这正是生产者消费者模型的核心同步逻辑。
下面我们将使用Linux POSIX线程库(pthread)来实现一个基于阻塞队列的生产消费者模型。完整代码如下:
#include #include #include #include #define BUFFER_SIZE 5// 阻塞队列结构typedef struct { int buffer[BUFFER_SIZE]; int in, out, count; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty;} BlockingQueue;// 初始化队列void queue_init(BlockingQueue *q) { q->in = q->out = q->count = 0; pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->not_full, NULL); pthread_cond_init(&q->not_empty, NULL);}// 放入数据(生产者调用)void queue_put(BlockingQueue *q, int data) { pthread_mutex_lock(&q->mutex); while (q->count == BUFFER_SIZE) { // 队列满,等待 pthread_cond_wait(&q->not_full, &q->mutex); } q->buffer[q->in] = data; q->in = (q->in + 1) % BUFFER_SIZE; q->count++; printf("生产者放入数据:%d,当前队列大小:%d", data, q->count); // 通知消费者队列非空 pthread_cond_signal(&q->not_empty); pthread_mutex_unlock(&q->mutex);}// 取出数据(消费者调用)int queue_get(BlockingQueue *q) { pthread_mutex_lock(&q->mutex); while (q->count == 0) { // 队列空,等待 pthread_cond_wait(&q->not_empty, &q->mutex); } int data = q->buffer[q->out]; q->out = (q->out + 1) % BUFFER_SIZE; q->count--; printf("消费者取出数据:%d,当前队列大小:%d", data, q->count); // 通知生产者队列非满 pthread_cond_signal(&q->not_full); pthread_mutex_unlock(&q->mutex); return data;}// 生产者线程函数void* producer(void *arg) { BlockingQueue q = (BlockingQueue)arg; int i; for (i = 1; i <= 10; i++) { queue_put(q, i); usleep(rand() % 500000); // 随机休眠,模拟生产耗时 } return NULL;}// 消费者线程函数void* consumer(void *arg) { BlockingQueue q = (BlockingQueue)arg; int i; for (i = 1; i <= 10; i++) { int data = queue_get(q); usleep(rand() % 800000); // 随机休眠,模拟消费耗时 } return NULL;}int main() { BlockingQueue q; queue_init(&q); pthread_t prod, cons; // 创建生产者和消费者线程 pthread_create(&prod, NULL, producer, &q); pthread_create(&cons, NULL, consumer, &q); // 等待线程结束 pthread_join(prod, NULL); pthread_join(cons, NULL); // 销毁互斥锁和条件变量 pthread_mutex_destroy(&q.mutex); pthread_cond_destroy(&q.not_full); pthread_cond_destroy(&q.not_empty); return 0;} 上述代码定义了一个阻塞队列(BlockingQueue),内部包含一个环形缓冲区、两个指针(in和out)、当前元素计数count,以及一把互斥锁和两个条件变量(not_full和not_empty)。互斥锁保证对共享数据的互斥访问,条件变量则负责线程的阻塞与唤醒。
not_full条件(pthread_cond_wait会原子性地释放锁并阻塞,被唤醒后重新获得锁)。放入数据后,增加计数,并发出not_empty信号,最后解锁。not_empty条件,取出数据后发出not_full信号。这种设计确保了无论生产者和消费者的速度如何,都不会出现数据竞争或死锁,完美体现了多线程同步的精髓。
编译运行上述程序(需要链接pthread库:gcc -o prodcons prodcons.c -lpthread),你会看到类似如下的输出(顺序可能因随机休眠而不同):
生产者放入数据:1,当前队列大小:1生产者放入数据:2,当前队列大小:2消费者取出数据:1,当前队列大小:1生产者放入数据:3,当前队列大小:2消费者取出数据:2,当前队列大小:1...
当队列满(大小为5)时,生产者会阻塞直到消费者取出数据;当队列空时,消费者会阻塞直到生产者放入数据。这正是生产者消费者模型的预期行为,也验证了Linux 阻塞队列和条件变量的正确性。
本文通过一个完整的实例,详细讲解了Linux 阻塞队列如何实现经典的生产者消费者模型,涵盖了互斥锁、条件变量等多线程同步机制。读者可以基于此代码进一步扩展,例如增加多个生产者和消费者、实现超时等待、或使用C++的std::condition_variable等。掌握这个模型,将为深入学习并发编程打下坚实基础。
本文由主机测评网于2026-03-15发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/20260331229.html