生产者、消费者模式是我们在编程时经常用到的一个编程方法。
生产者是生产数据,提供原料的一方;消费是消耗数据,处理原料的一方。
缓冲区的意义
缓冲区一般是充当一个数据中转站的作用,使得生产数据和处理数据双方解耦合,各自分工。根据生产者和消费者的能力,进行均衡调整,使得系统高效配合工作。
电脑中的缓冲例子:当CPU需要去读取磁盘数据时,因为CPU和磁盘的读写速度相差几个数量级,这样会导致CPU处于空等待状态,所以采用了一个缓冲机制,先将磁盘数据放到缓冲区,当数据读取完成后,再通知CPU从缓冲中取数据。从而避免了CPU的无效等待,提高系统效率。
供求关系
缓冲区的大小是有限的,如果生产速度和消费速度不平衡,则会逐渐走向极端(缓冲区常空或常满)。
1)供 > 求(生产能力强),缓冲区数据逐渐堆积,最终生产者闲置;
2)供 < 求(消费能力强),缓冲区数据消耗快,最终消费者闲置;
3)供 = 求,生产和消耗速度达到平衡,缓冲区的数据量会稳定在一个范围。
供求平衡是我们追求的较理想的状态,但实际中生产能力和消费能力往往是存在差异的,需要我们根据实际的问题进行优化。例如如果生产能力过剩,则我们可以增加消费者线程,或抑制生产能力。(看到这里其实不难想到,该模式与生活中的市场问题是类似的,处理市场供求关系的一些手段也可以用于管理缓冲区)
在无法达到平衡时,该如何取舍?
让消费速度大于生产速度,虽然消费者会闲置,但是可以减少数据滞留时间。
多样的生产-消费关系
我们在使用多线程对这类关系进行编码,可以是只有一个生产者线程,一个消费者线程;也可以是一个生产者线程,同时有多个消费者线程处理数据。以及其它多生产多消费的复杂场景。(缓冲区只有一个)
甚至于有时候,一个线程可以是当前任务的消费者,也是下一个任务的生产者。多个线程组成一个生产-消费关系的链条,类似工厂流水线(组装 — 测试 — 包装)。
在生产消费者模式链中,制约系统运行效率的往往是处理最慢的那个环节,将每个环节比喻为一个个管道,多个小管道拼接成为一个长的管道,水流速度往往取决于最窄也就是速度最慢的那一截管道。有点类似木桶效应。所以我们平时在处理优化问题时,应该找到制约效率的关键位置,然后针对性的处理,才能大大提高系统效率。
线程池编程实例
现在有一个任务:找出LEFT~RIGHT范围内的质数有哪些,并打印出来。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (4)
// 线程池实现,main线程分发数字(任务),>0为有任务,=0当前任务被领取,=-1没有任务可以分发了;
// 其它线程抢任务,计算完成后将num清0;如果num为-1, 线程跳出循环结束。
static pthread_mutex_t mut_num = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num = PTHREAD_COND_INITIALIZER;
static int num = 0;
static void* thr_primer(void *p)
{
int i,j,mark;
int n = (int)p;
while(1)
{
pthread_mutex_lock(&mut_num); //抢锁
while(num == 0)
{
pthread_cond_wait(&cond_num,&mut_num); //等待任务下发或结束
}
if(num == -1)
{
pthread_mutex_unlock(&mut_num); //解锁
break;
}
i = num;
num = 0;
pthread_cond_broadcast(&cond_num); //广播通知任务被领取
pthread_mutex_unlock(&mut_num); //解锁
// 领到数字,判断是否是质数
mark = 1;
for(j = 2; j < i/2; j++)
{
if(i % j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("[%d]:%d is primer\n",n,i);
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[THRNUM];
int err,i;
for(i = 0; i < THRNUM; i++) // 创建线程池
{
err = pthread_create(tid+i,NULL,thr_primer,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for(i = LEFT; i <= RIGHT; i++)
{
pthread_mutex_lock(&mut_num);
while(num != 0)
{
pthread_cond_wait(&cond_num,&mut_num); //等待任务被线程领取
}
num = i;
pthread_cond_signal(&cond_num); //通知下游线程,有任务分发下来
pthread_mutex_unlock(&mut_num); //解锁
}
pthread_mutex_lock(&mut_num); //抢锁
num = -1; //提醒线程结束
pthread_cond_broadcast(&cond_num); //通知下游所有线程退出
pthread_mutex_unlock(&mut_num);
//回收线程
for(i = 0; i < THRNUM; i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mut_num);
pthread_cond_destroy(&cond_num);
exit(0);
}
有问题欢迎私信或留言讨论,如果觉得内容还可以的话麻烦点赞支持一下。