生产者消费者模型(一)(模拟单线程的互斥与同步)

2017-06-01 13:05 阅读 207 次 评论 0 条

在实现的软件开发过程中,经常会碰到如下情景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程和进程等)。产生数据的模块就称之为生产者,而处理数据的模块,就是消费者

条件变量

在正式开始生产者与消费者模型之前,我们应该对条件变量有一个新的认识。

条件变量(Condition Variable):它的作用是描述资源的就绪状态,属于线程的一种同步机制。互斥锁用于上锁,条件变量则用于等待。条件变量本身是由互斥量保护的,线程在改变条件之前首先会封锁互斥量,因此其他线程在获得互斥量之前不会察觉到这样的变化。

① pthread_cond_init和pthread_cond_destroy:条件变量的初始化与销毁。

参数cond:条件变量。

参数attr:条件变量的属性,设置为NULL则表示缺省属性,

返回值:成功返回0,失败返回错误编号。

mutex的初始化和销毁类似,如果条件变量是静态分配的,也可以使用PTHEAD_COND_INITIALIZER初始化,相当于用pthread_cond_init函数初始化并设置参数attr为NULL。

② pthread_cond_wait/timewait:前者是条件变量等待,后者是等待超时。

参数cond:条件变量。

参数mutex:互斥锁。

返回值:成功返回0,失败返回错误编号。

pthread_cond_timewait函数还有一个额外的参数可以设定等待超时,如果到达了abstime所指定的时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。

可见,一个Condition Variable总是和一个mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个条件变量上阻塞等待,这个函数需要以下三步操作:

1)释放mutex   2)阻塞等待 3)当被唤醒时,重新获得mutex并返回。

③ pthread_cond_broadcast/signal:唤醒线程。前者是唤醒进程内的所有线程,后者是唤醒一个线程。

返回值:成功返回0,失败返回错误编号。

一个线程可以调用pthread_cond_signal唤醒在某个条件变量上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个条件变量上等待的所有线程。

生产者消费者模型

生产者与消费者牵扯最多的是多线程的同步问题,单单抽象出生产者和消费者,还够不上生产者消费者模。因此该模式需要加入一个缓冲区作为二者的媒介,生产者把数据放入缓冲区,消费者从缓冲区取走数据,结构图如下:

生产者与消费者模型的3-2-1规则:3个关系,2个角色,1个交易场所(本篇使用链表模拟)。

后两个一目了然,我主要说一下三种关系:

① 生产者与生产者(互斥)

② 生产者与消费者(互斥+同步)

③ 消费者与消费者(互斥)

也就是说,生产者生产的时候消费者不能消费,消费者消费的时候生产者同样不能生产。缓冲区为空时,消费者不能消费,缓冲区满时,生产者也不能再生产。

生产者消费者模型的三大特性

▶解耦(软件工程追求高内聚、低耦合)

假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(耦合)。将来如果消费者的代码出现变化,可能会影响到生产者。但是如果二者均依赖于同一个方法(缓冲区),耦合自然就降低了。

▶支持并发(最主要的特性)

由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只能一直等待,如果消费者的处理速度很慢,生产者则会浪费很多时间。因此,生产者消费者模型就提出了缓冲区的概念,即生产者将数据放入缓冲区后,可以继续生产,将不再依赖于消费者的处理速度。

▶支持忙闲不均

缓冲区的另一个好处在于,生产数据的速度时快时慢,当数据制造快的时候,如果消费者来不及处理,未处理的数据就可以存放进缓冲区,消费者再慢慢处理。

基于单线程的生产者消费者模型

我们使用两个线程分别模拟生产者(producter)和消费者(consumer),底层数据结构借助单链表作为缓冲区,生产者通过向链表插入节点,消费者从链表中删除节点来模拟生产者与消费者的行为。

从结果可以看出,生产者生产一个,消费者消费一个,循环交替,实现了基于单线程的互斥与同步的生产者消费之模型。

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:生产者消费者模型(一)(模拟单线程的互斥与同步) | 术与道的分享
1024do.com导航_术与道导航平台

发表评论


表情