Linux使用互斥锁和条件变量实现生产者消费者模型

1、问题描述

  • 现有一个链表作为产品储存区,这个链表为临界资源,生产者和消费者都要访问,
  • 5个生产者线程不断生产新的节点插入链表中
  • 5个消费者线程不断从链表中读取并删除节点

生产者和消费者需要抢临界资源的互斥锁,并且消费者在消费之前如果发现没有产品,消费者线程就要被阻塞,如果生产者生产出了新产品就要唤醒阻塞的消费者线程

2、思路

  • 创建一把互斥锁管理产品储存区,生产者和消费者互斥访问
  • 创建一个条件变量,如果消费者消费时发现没有产品,就要被这个条件变量阻塞,生产者生产出了产品就要通过这个条件变量唤醒被阻塞的消费者

3、代码实现

#include 
#include 
#include 
#include 
#include 

//定义条件变量
pthread_cond_t cond;
//定义互斥锁
pthread_mutex_t mutex;

//定义链表结构体
struct Node
{
	int number;
	struct Node* next;
};

//定义头结点
struct Node* head = NULL;
//当前产品总数
int num = 0;

//生产者线程函数
void* producer(void* arg)
{
	while(1)
	{
        //上锁
		pthread_mutex_lock(&mutex);

		struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); //新节点
		newNode->number = rand() % 1000 + 1;
		newNode->next = head; //头插法
		head = newNode;
		num++;
		printf("生产者, id:%ld, number:%d, 剩余产品数:%d\n"
                , pthread_self(), newNode->number, num);
        
        //解锁
		pthread_mutex_unlock(&mutex);
        
        //唤醒所有被条件变量阻塞的消费者线程
		pthread_cond_broadcast(&cond);

		sleep(rand() % 3);  //把CPU时间片交给其他线程,不然执行太快了看不到效果
	}
	return NULL;
}
//消费者
void* comsumer(void* arv)
{
	while(1)
	{
        //上锁
		pthread_mutex_lock(&mutex);

        //当头结点为空时说明产品区为空
		while(head == NULL)
		{
			pthread_cond_wait(&cond, &mutex);  //阻塞当前线程,等待生产者唤醒
		}
		struct Node* node = head;
		head = head->next; //删头
		num--;
		printf("消费者, id:%ld, number:%d, 剩余产品数:%d\n"
                , pthread_self(), node->number, num);
		free(node);

        //解锁
		pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}
	return NULL;
}
int main()
{
    //初始化互斥锁和条件变量
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
	
    //创建线程
	pthread_t t1[5], t2[5];
	for (int i = 0; i < 5; i++)
	{
		pthread_create(&t1[i], NULL, producer, NULL);
	}

	for (int i = 0; i < 5; i++)
	{
		pthread_create(&t2[i], NULL, comsumer, NULL);
	}

	//回收线程资源,由于生产者线程和消费者线程都是死循环,主线程被阻塞在这里了
	for (int i = 0; i < 5; i++)
	{
		pthread_join(t1[i], NULL);
		pthread_join(t2[i], NULL);
	}

    //销毁互斥锁和条件变量
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
	return 0;
}

关键问题:

  1. 消费者线程中pthread_cond_wait()函数要放在while循环中而不是if中,这是因为当所有消费者被阻塞在这里的时候,如果这时候有生产者生产出了一个产品,就会通知所有被阻塞的消费者线程,这些消费者线程抢夺互斥锁,抢到的线程把互斥锁上锁,进入临界区进行消费,消费完后如果依然是消费者抢到了时间片,如果这里使用的是if,那么抢到互斥锁的消费者就会进入临界区,对空的产品区消费,引发错误。因此,需要使用while循环,再判断一次产品区是否为空。
  2. 生产者每生产一个产品就通知所有消费者线程,如果这时候有被条件变量阻塞的消费者线程,那么就会被唤醒,如果没有也不影响程序的继续执行

本文采用互斥锁和条件变量,还有采用互斥锁和信号量实现生产者消费者模型

(本人学习苏老师课程的笔记和感悟,来源如下:)

作者: 苏丙榅
链接: https://subingwen.cn/linux/thread-sync/#5-1-%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F%E5%87%BD%E6%95%B0
来源: 爱编程的大丙
 

你可能感兴趣的:(linux,c语言,多线程)