实战开发经验: 软件中的缓冲区管理

简介:

1. 前言


什么是缓冲区管理策略?为什么要使用缓冲区管理策略,这里首先引用几段《生产者/消费者模式》中的文字作为引子吧。

        

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

单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。大概的结构如下图:                                   

 

      

 

这种模式最典型的应用就是网络数据的传送,对应的生产者为文件操作者,只负责读取数据文件填充到缓冲区中,对应的消费者为网络发送者,只管从缓冲区中取出数据,发送到对应的接收端,大概结构如下:

       

    

 

这样做有什么好处呢?
        

(1) 解耦


假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。


(2)支持并发
        

生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。
使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。
       

(3)支持忙闲不均

        

缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

当然,还有很多其他的好处,这里就不一一列举了。下面进行本文讨论的重点,即怎么样实现和管理这样一个缓冲区呢?怎样才能很好的保证缓冲区容量的平衡,不至于溢出也不会经常性为空?


2. 什么是缓冲区管理策略

典型的缓冲区如下图:


       

                            

If(Vi > Vo)则一定时间后,缓冲区肯定会溢出(如果你说将缓冲区设得足够大就没问题,但那样会占用太多系统资源,并不可取);同理,If(Vi < Vo),则一定时间后,缓冲区肯定会为空,导致数据消耗者得不到数据。而且在实际的应用中,Vi和Vo也不是定值,也常常会动态的变化(例如:假设消费者是网络发送者,在网络状况比较差的情况时,它消耗缓冲区的速率可能就小于我们设定的Vo),故我们是无法通过实现Vi = Vo这种方式来保证缓冲区的平衡,那么,怎样保证消费者每次都能取到数据、生产者填充数据不会导致溢出呢?
        

这里,我们需要的就不仅仅是一个简简单单静态的缓冲区,而是一个缓冲区管理者,由该缓冲区使用一定的策略管理该缓冲区,以避免上述情况的发生。


3. 怎么样实现缓冲区管理策略

如上所述,我们不能保证Vi准确地等于Vo,那么我们可以换一个思路,假设缓冲区满记为100%,缓冲区空记为0%,那么我们其实可以通过保证缓冲区的数据容量在50%左右这样一个指标来实现缓冲区不为空又不至于溢出这样一个梦想,如下图所示:

 

       

 

       

我们称当前缓冲区填充的数据量百分比为“水位”,设计两个门限值,当水位低于30%时,我们称为“低水位”状态,当水位高于70%时,我们称为“高水位”状态。我们的目标是使缓冲区中的水位保持在30%到70%之间,一旦到达“低水位”或者“高水位”状态,则启用“水位管理”,通知生产者加快或者减慢生产速率,或者通知消费者减慢或者加快消费速率,以便达到维持缓冲区平衡的目的。


“水位管理”方案

 

(1)缓冲区刚开始为空时,由于处于“低水位”状态,故采用的是“低水位门限控制方案”,即若消费者消耗速率为Vo,生产者则以速率Vo的n倍(例如2倍)填充缓冲区,直到水位到达50%时,再回到消费的速率Vo来填充缓冲区。


(2)缓冲区由于填充速率过低,或者消耗速率过高,导致进入“低水位”状态时,处理方法同上,依然采用低水位门限控制方案。

       

(3)当缓冲区由于消耗过慢,或者填充过快,导致进入“高水位”状态时,则采用“高水位门限控制方案”,即若消费者消耗的速率为Vo,生产者则以速率Vo的1/n倍(例如1/2倍)来填充缓冲区,直到水位降到50%时,再回到消费的速率Vo来填充缓冲区。

         

请思考:为什么要在50%处才将恢复原速率?
           

在此,为了方便理解,形象地用下面这张图来描述上面的管理策略之“低水位门限控制方案”:

                    

         

4. 其他说明


下面就几个可能的疑问做出一些解释。
       

(1) 程序中如何实现速率控制,比如读取文件数据的速率?
一般生产者要自动生产数据是靠线程实现的,而在线程中读取文件并且填充缓冲区一般是靠定时器来实现的(当然,也可以使用Sleep的方式)。这样一来,其实有两种控制速率的方案,一种是通过改变填充缓冲区的定时器的定时时间间隔来实现速率的控制,另外一种是改变每次读取和填充的字节数,来间接实现改变填充缓冲区的速率。
      

(2) 生产者和消费者同时访问缓冲区会有问题吗?
当然,缓冲区的访问肯定要使用加锁或者信号量这样的保护措施。
      

(3) 其他疑问欢迎大家提出来。




本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/273133,如需转载请自行联系原作者
相关文章
|
3月前
有关学习如何管理团队的书籍推荐
有关学习如何管理团队的书籍推荐
38 0
|
1月前
|
关系型数据库 数据库 PostgreSQL
已知成长器软件用户手册
已知成长器软件用户手册
|
4月前
|
前端开发
前端小白如何开发新项目(速成版)
前端小白如何开发新项目(速成版)
81 0
|
4月前
|
编译器 程序员 Linux
嵌入式软件开发第一讲笔记
嵌入式软件开发第一讲笔记
30 0
|
7月前
|
Web App开发 存储 缓存
5款精挑细选的软件,助你事半功倍
在工作的时候,大家都喜欢通过一些好用有效率的工具,来让工作更加快速地完成,今天给大家带来的这5款软件,更是一款比一款还要惊喜!
50 0
|
敏捷开发 运维 监控
技术分享 | 想测试入门就必须要懂的软件开发流程
技术分享 | 想测试入门就必须要懂的软件开发流程
|
敏捷开发 运维 监控
技术分享 | 想测试入门就必须要懂的软件开发流程
从事软件测试行业,每天面对的被测对象都是软件。如果想要更好的去完成测试工作,首先需要对被测对象,也就是对软件要有基本的了解。 ## 软件 与计算机系统操作有关的计算机程序、可能有的文件、文档及数据。 程序好理解,就是可以操作的产品。比如 wps、微信、QQ、网页等等这些都是程序。比如说需求文档、设计文档、用户手册这些东西都属于文档。在页面中展示的,还有用户输入的内容这些都是数据。 所以说程
|
弹性计算 运维 应用服务中间件
阿里云学生机入门指南(开发者成长计划)
现在国内知名大牌云服务商(阿里云)对学生都是很优惠的,阿里云学生服务器
826 0
|
设计模式 Java 关系型数据库
《软件开发实践:项目驱动式的Java开发指南》正式出版(译著)
在疫情期间,我接手了这本书的翻译工作,由于不能经常外出,所以竟然提前稿,最终在出版社漫长的三审三校下,终于在本月出版。这里先放出我写的译者序,大家可以从中了解下本书内容。
858 0
《软件开发实践:项目驱动式的Java开发指南》正式出版(译著)
|
搜索推荐 Java
独家下载!《Java开发手册》灵魂13问,深度剖析一线大厂开发思维 | 技术日报(9期)
《〈Java开发手册(泰山版)〉灵魂13问》独家首发!全网千万阅读量技术博主深度剖析Java规约背后的原理,从“问题重现”到“原理分析”再到“问题解决”,下载《Java开发手册》必备的伴读书目!
3107 0