当前位置:首页>Linux>Linux多线程编程:用互斥锁与条件变量解决多生产者-多消费者问题

Linux多线程编程:用互斥锁与条件变量解决多生产者-多消费者问题

  • 2026-10-10 14:47:09
Linux多线程编程:用互斥锁与条件变量解决多生产者-多消费者问题

大家好,我是一个爱分享的牛马程序员,工作中碰到,加上自己理解,很高兴给大家分享。

-begin-

题目:在Linux系统中,编写一个程序,使用线程(pthread)和互斥锁(mutex)实现“多生产者-多消费者”问题:创建2个生产者线程和3个消费者线程,共享一个大小为5的缓冲区。生产者线程循环生成1-20的随机整数放入缓冲区,消费者线程从缓冲区取出整数并打印,当所有生产者生成完20个整数后,所有线程退出。

详细分析过程:

1.线程与同步机制设计:
◦生产者线程:2个线程并发执行,各自生成10个整数(共20个),放入缓冲区时需保证互斥访问。
◦消费者线程:3个线程并发执行,从缓冲区取数并打印,取数时同样需互斥。
◦同步工具:使用pthread_mutex_t作为互斥锁,保证缓冲区操作的原子性;使用pthread_cond_t创建两个条件变量(not_empty和not_full),分别用于消费者等待数据和生产者等待空位。
2.缓冲区与全局变量:
◦缓冲区:大小为5的数组,采用循环队列结构,用in和out索引记录存取位置。
◦全局变量:count记录缓冲区内数据个数,total_produced记录已生成的整数总数(用于判断是否结束),buffer存储数据。
3.线程逻辑:
◦生产者线程:循环生成随机数,先加锁,若缓冲区满则等待not_full条件变量;否则放入数据,更新in和count,唤醒not_empty条件变量,解锁。生成满10个数后退出。
◦消费者线程:循环取数,先加锁,若缓冲区空则等待not_empty条件变量;否则取出数据,更新out和count,唤醒not_full条件变量,解锁并打印。当total_produced达到20且缓冲区空时退出。
4.资源清理:所有线程退出后,销毁互斥锁和条件变量,释放资源。

关键代码:

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <unistd.h>

#include <time.h>

#define BUFFER_SIZE 5  // 缓冲区大小

#define MAX_NUM 20     // 总生成数

#define PRODUCERS 2    // 生产者数量

#define CONSUMERS 3    // 消费者数量

#define PRODUCER_NUM (MAX_NUM / PRODUCERS)  // 每个生产者生成数

// 缓冲区及控制变量

int buffer[BUFFER_SIZE];

int in = 0;          // 生产者放入位置

int out = 0;         // 消费者取出位置

int count = 0;       // 缓冲区内数据个数

int total_produced = 0;  // 已生成总数

// 同步机制

pthread_mutex_t mutex;

pthread_cond_t not_empty;  // 缓冲区非空条件

pthread_cond_t not_full;   // 缓冲区非满条件

// 生产者线程函数

void *producer(void *arg) {

   int id = *(int *)arg;

   free(arg);  // 释放动态分配的ID

   for (int i = 0; i < PRODUCER_NUM; i++) {

       // 生成1-MAX_NUM的随机数

       int num = rand() % MAX_NUM + 1;

       usleep(rand() % 500000);  // 随机延时模拟生产耗时

       pthread_mutex_lock(&mutex);

       // 若缓冲区满,等待非满条件

       while (count == BUFFER_SIZE) {

           pthread_cond_wait(&not_full, &mutex);

       }

       // 放入数据

       buffer[in] = num;

       printf("生产者%d:放入 %d(位置:%d,当前数量:%d)\n", id, num, in, count + 1);

       in = (in + 1) % BUFFER_SIZE;

       count++;

       total_produced++;

       pthread_cond_signal(&not_empty);  // 唤醒等待非空的消费者

       pthread_mutex_unlock(&mutex);

   }

   printf("生产者%d:完成生产,退出\n", id);

   pthread_exit(NULL);

}

// 消费者线程函数

void *consumer(void *arg) {

   int id = *(int *)arg;

   free(arg);  // 释放动态分配的ID

   while (1) {

       pthread_mutex_lock(&mutex);

       // 若缓冲区空且生产完毕,退出

       while (count == 0 && total_produced >= MAX_NUM) {

           pthread_mutex_unlock(&mutex);

           printf("消费者%d:无数据可消费,退出\n", id);

           pthread_exit(NULL);

       }

       // 若缓冲区空,等待非空条件

       while (count == 0) {

           pthread_cond_wait(&not_empty, &mutex);

       }

       // 取出数据

       int num = buffer[out];

       printf("消费者%d:取出 %d(位置:%d,当前数量:%d)\n", id, num, out, count - 1);

       out = (out + 1) % BUFFER_SIZE;

       count--;

       pthread_cond_signal(&not_full);  // 唤醒等待非满的生产者

       pthread_mutex_unlock(&mutex);

       usleep(rand() % 500000);  // 随机延时模拟消费耗时

   }

}

int main() {

   pthread_t prod_tids[PRODUCERS];

   pthread_t cons_tids[CONSUMERS];

   srand(time(NULL));  // 初始化随机数种子

   // 初始化同步机制

   pthread_mutex_init(&mutex, NULL);

   pthread_cond_init(&not_empty, NULL);

   pthread_cond_init(&not_full, NULL);

   // 创建生产者线程

   for (int i = 0; i < PRODUCERS; i++) {

       int *id = malloc(sizeof(int));

       *id = i + 1;  // 生产者ID:1,2

       if (pthread_create(&prod_tids[i], NULL, producer, id) != 0) {

           perror("pthread_create producer failed");

           exit(EXIT_FAILURE);

       }

   }

   // 创建消费者线程

   for (int i = 0; i < CONSUMERS; i++) {

       int *id = malloc(sizeof(int));

       *id = i + 1;  // 消费者ID:1,2,3

       if (pthread_create(&cons_tids[i], NULL, consumer, id) != 0) {

           perror("pthread_create consumer failed");

           exit(EXIT_FAILURE);

       }

   }

   // 等待所有生产者线程结束

   for (int i = 0; i < PRODUCERS; i++) {

       pthread_join(prod_tids[i], NULL);

   }

   // 唤醒所有等待的消费者(确保它们检查退出条件)

   pthread_mutex_lock(&mutex);

   pthread_cond_broadcast(&not_empty);

   pthread_mutex_unlock(&mutex);

   // 等待所有消费者线程结束

   for (int i = 0; i < CONSUMERS; i++) {

       pthread_join(cons_tids[i], NULL);

   }

   // 清理资源

   pthread_mutex_destroy(&mutex);

   pthread_cond_destroy(&not_empty);

   pthread_cond_destroy(&not_full);

   printf("所有线程结束,程序退出\n");

   return 0;

}

代码说明:

•线程创建:通过pthread_create创建多个生产者和消费者线程,传递唯一ID用于区分。每个生产者生成10个数,总生成数为20。
•互斥与条件变量:mutex保证缓冲区操作的互斥性;not_empty在缓冲区有数据时唤醒消费者,not_full在缓冲区有空位时唤醒生产者。使用while循环判断条件(而非if),避免虚假唤醒(因系统信号等意外唤醒线程)。
•退出逻辑:生产者完成自身任务后退出;消费者在总生成数达标且缓冲区为空时退出,主线程通过pthread_join等待所有线程结束。
•随机延时:模拟实际生产和消费的耗时差异,体现多线程并发的随机性。

运行结果示例(片段):

生产者1:放入 5(位置:0,当前数量:1)

消费者1:取出 5(位置:0,当前数量:0)

生产者2:放入 12(位置:1,当前数量:1)

生产者1:放入 3(位置:0,当前数量:2)

消费者2:取出 12(位置:1,当前数量:1)

...

生产者2:完成生产,退出

生产者1:完成生产,退出

消费者3:取出 18(位置:3,当前数量:0)

消费者1:无数据可消费,退出

消费者2:无数据可消费,退出

消费者3:无数据可消费,退出

所有线程结束,程序退出

常见考点:

•多线程同步(互斥锁+条件变量的配合使用);
•条件变量的pthread_cond_wait(必须在加锁后调用,自动释放锁并等待)和pthread_cond_signal/broadcast(唤醒等待线程);
•线程退出条件的设计(避免消费者无限等待);
•多生产者/消费者并发时的数据一致性保证。
-end-

如果文章对你有提升,帮忙点赞,分享,关注。十分感谢

最新文章

随机文章