最近在看surfaceflinger这块内容时,看到了生产者/消费者模式的影子,其实生产者/消费者模式在Android源码中无处不在,借此机会,简单梳理下这个模式,本文不会去关注具体代码细节,而是去阐述这个思想,理解思想至关重要。
为何引入生产者/消费者模式?
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
介绍surfaceflinger中的生产者/消费者模式之前,先举例一个简单的例子,以便对生产者/消费者有个认识
image在多线程环境下,BlockingQueue能够处理线程间安全问题。在BlockingQueue队列为空的时候,消费者端的所有线程会自动处于等待(挂起)状态,直到有数据放入队列。
当BlockingQueue队列已满的时候,生产者端的线程都会被挂起,直到队列有空间,生产者线程会自动被唤醒。
所以在多线程环境下,BlockingQueue能够自动挂起和唤醒线程,不需要关心阻塞和唤醒的问题,所有的事情BlockingQueue已经帮我们处理了,我们只需要知道如何使用它。
显示系统中“生产者-消费者”模型
这里会主要涉及如下三个概念
Ø BufferQueue
BufferQueue是buffer流转重要中转站,可以认为BufferQueue是一个服务中心,其它两个owner必须要通过它来管理buffer。比如说当producer想要获取一个buffer时,它不能越过BufferQueue直接与consumer进行联系,反之亦然。
Ø Producer
生产者就是“填充”buffer空间的人,通常情况下当然就是应用程序。当然了应用程序不会直接使用BufferQueue,和Android系统很多地方一样,“层层封装”在这里同样存在。因为应用程序不断地刷新UI,从而将产生的显示数据源源不断地写到buffer中。
当Producer需要使用一块buffer时,它首先会向中介BufferQueue发起dequeue申请,然后才能对指定的缓冲区进行操作。这种情况下buffer就属于producer一个人的了,它可以对buffer进行任何必要的操作,而其它owner此刻绝不能擅自插手。
当生产者认为一块buffer已经写入完成后,它进一步调用BufferQueue的queue。从字面上看这个函数是“入列”的意思,形象地表达了buffer此时的操作——把buffer归还到BufferQueue的队列中。一旦queue成功后,owner也就随之改变为BufferQueue了
Ø Consumer
消费者是与生产者相对应的,它的操作同样受到BufferQueue的管控。当一块buffer已经就绪后,Consumer就可以开始工作了。这里需要特别留意的是,从各个对象所扮演的角色来看,BufferQueue是中介机构,属于服务提供方。Producer属于buffer内容的产出方,它对缓冲区的操作是一个“主动”的过程;反之,Consumer对buffer的处理则是“被动”的、“等待式”的——它必须要等到一块buffer填充完成后才能做工作。
在这样的模型下,我们怎么保证Consumer可以及时的处理buffer呢?换句话说,当一块buffer数据ready后,应该怎么告知Consumer来操作呢?当有一帧数据准备就绪后,BufferQueue就会调用onFrameAvailable()来通知Consumer进行消费。
一块Buffer大致经历的过程就是FREE->DEQUEUED->QUEUED->ACQUIRED->FREE。从owner的角度来讲,于下图的描述
image小结:
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,这个阻塞队列就是用来给生产者和消费者解耦的。
本文部分内容参考如下:
《深入理解Android内核设计思想》
网友评论