生产者与消费者模型
生产者:生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据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);
}