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

生产者与消费者模型

**生产者:**生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据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);
}
相关推荐
律宏阔2 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔2 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴2 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千3 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei373 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei373 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei373 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW3 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW3 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei373 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化