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

生产者与消费者模型

**生产者:**生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据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);
}
相关推荐
Komorebi.py13 分钟前
【Linux】-学习笔记05
linux·笔记·学习
Mr_Xuhhh18 分钟前
重生之我在学环境变量
linux·运维·服务器·前端·chrome·算法
内核程序员kevin3 小时前
TCP Listen 队列详解与优化指南
linux·网络·tcp/ip
朝九晚五ฺ8 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream8 小时前
Linux的桌面
linux
xiaozhiwise8 小时前
Makefile 之 自动化变量
linux
意疏11 小时前
【Linux 篇】Docker 的容器之海与镜像之岛:于 Linux 系统内探索容器化的奇妙航行
linux·docker
BLEACH-heiqiyihu11 小时前
RedHat7—Linux中kickstart自动安装脚本制作
linux·运维·服务器
一只爱撸猫的程序猿11 小时前
一个简单的Linux 服务器性能优化案例
linux·mysql·nginx