操作系统(线程管理-通过条件变量实现消费者与生产者模型)

生产者与消费者模型

**生产者:**生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据Push到存储中介。

**消费者:**负责消耗数据的线程,对生产者线程生产的数据进行(判断、筛选、使用、响应、存储)处理。

**存储中介:**也叫数据仓库,是生产者线程与消费者线程之间的数据缓冲区,用于平衡二者之间的生产速度与消耗速度不均衡的问题,通过缓冲区隔离生产者和消费者,与二者直连相比,避免相互等待,提高运行效率。

**问题1:**生产快于消费,缓冲区满,撑死。

解决方法:负责生产的线程通知负责消费的线程全速消费,然后进入休眠。

**问题2:**消费快于生产,缓冲区空,饿死。

解决方法:负责消费的线程通知负责生产的线程全速生产,然后进入休眠。

条件变量

条件变量是利用线程间共享的"全局变量"进行同步的一种机制,主要包括两个动作:

1、线程等待"条件变量的条件成立"而休眠;

2、等"条件成立"叫醒休眠的线程。

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起,一般线程睡入条件变量,伴随着解锁动作,而线程从条件变量醒来时,伴随着加锁动作,如果加锁失败线程进入阻塞状态,而不是睡眠。

复制代码
// 定义或创建条件变量
pthread_cond_t cond;
​
// 初始化条件变量
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
//亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
​
// 使调用线程睡入条件变量cond,同时释放互斥锁mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);
​
// 带倒计时的睡眠,时间到了会自动醒来
int pthread_cond_timedwait (pthread_cond_t* cond,
    pthread_mutex_t* mutex,
    const struct timespec* abstime);
​
struct timespec {
    time_t tv_sec;  // Seconds
    long   tv_nsec; // Nanoseconds [0 - 999999999]
};
​
// 从条件变量cond中叫醒一个线程,令其重新获得原先的互斥锁
int pthread_cond_signal (pthread_cond_t* cond);
注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
​
// 从条件变量cond中唤醒所有线程
int pthread_cond_broadcast (pthread_cond_t* cond);
​
// 销毁条件变量
int pthread_cond_destroy (pthread_cond_t* cond);

注意:使用互斥锁配合条件变量实现的生产者与消费者模型,能够平衡生产与消费的时间不协调,并且可以最大限度的节约运行资源。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define STORE_CAP (20)
char store[STORE_CAP];
int front = 0, rear = 0;

void show_store(const char* opt,char data)
{
	for(int i=front; i!=rear; i=(i+1)%STORE_CAP)
	{
		printf("%c ",store[i]);
	}
	printf("%s %c\n",opt,data);
}

// 保护仓库的互斥锁
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 当仓库空时,消费者线程睡入的条件变量 
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;
// 当仓库满时,生产者线程睡入的条件变量
pthread_cond_t full = PTHREAD_COND_INITIALIZER;

void* p_run(void* arg)
{
	// 循环的生产数据
	for(;;)
	{
		// 加锁保护仓库
		pthread_mutex_lock(&lock);

		while((rear+1)%STORE_CAP == front)
		{
			// 叫醒所有的消费者线程,全速消费
			pthread_cond_broadcast(&empty);

			// 当前生产者线程睡入 full 条件变量,并解锁
			pthread_cond_wait(&full,&lock);
		}

		// 随机生产一个数据并入库
		char data = rand() % 26 + 'A';
		show_store("<-",data);
		store[rear] = data;
		rear = (rear+1)%STORE_CAP;

		// 叫醒一个消费者线程,开始消费
		pthread_cond_signal(&empty);

		// 解锁
		pthread_mutex_unlock(&lock);

		// 随机休眠一段时间模拟生产数据所消耗的时间
		usleep(rand()%100*5000);
	}
}

void* c_run(void* arg)
{
	// 循环的消费数据
	for(;;)
	{
		// 加锁保护仓库
		pthread_mutex_lock(&lock);

		while(front == rear)
		{
			// 叫醒所有生产者线程,全速生产
			pthread_cond_broadcast(&full);

			// 当前消费者线程睡入 empty 条件变量,并解锁
			pthread_cond_wait(&empty,&lock);
		}

		// 数据出库
		char data = store[front];
		front = (front+1)%STORE_CAP;
		show_store("->",data);

		// 叫醒一个生产者,开始生产数据
		pthread_cond_signal(&full);

		// 解锁
		pthread_mutex_unlock(&lock);

		// 随机休眠一段时间,模拟消费数据所需要的时间
		usleep(rand()%100*5000);
	}
}

int main(int argc,const char* argv[])
{
	size_t pthread_cnt = 10;
	pthread_t tids[pthread_cnt];

	for(int i=0; i<pthread_cnt; i++)
	{
		if(i%2)
			pthread_create(tids+i,NULL,p_run,NULL);
		else
			pthread_create(tids+i,NULL,c_run,NULL);
		pthread_detach(tids[i]);
	}	

	pthread_exit(NULL);
}
相关推荐
AndyFrank几秒前
mac crontab 不能使用问题简记
linux·运维·macos
筱源源16 分钟前
Kafka-linux环境部署
linux·kafka
算法与编程之美1 小时前
文件的写入与读取
linux·运维·服务器
xianwu5431 小时前
反向代理模块
linux·开发语言·网络·git
Amelio_Ming1 小时前
Permissions 0755 for ‘/etc/ssh/ssh_host_rsa_key‘ are too open.问题解决
linux·运维·ssh
Ven%2 小时前
centos查看硬盘资源使用情况命令大全
linux·运维·centos
TeYiToKu3 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws3 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
yeyuningzi3 小时前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
上辈子杀猪这辈子学IT4 小时前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian