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

生产者与消费者模型

**生产者:**生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据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);
}
相关推荐
无为之士5 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
岑梓铭21 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
努力学习的小廉22 分钟前
深入了解Linux —— make和makefile自动化构建工具
linux·服务器·自动化
MZWeiei25 分钟前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper
7yewh41 分钟前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
小张认为的测试1 小时前
Linux性能监控命令_nmon 安装与使用以及生成分析Excel图表
linux·服务器·测试工具·自动化·php·excel·压力测试
打鱼又晒网1 小时前
linux网络套接字 | 深度解析守护进程 | 实现tcp服务守护进程化
linux·网络协议·计算机网络·tcp
良许Linux1 小时前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
蜜獾云1 小时前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维1 小时前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构