[3]:环形缓冲区数组
前一个帖子说起了队列缓冲区可能存在的性能问题及解决方法:环形缓冲区。今天就专门来描述一下这个话题。性能
为了防止有人给咱扣上“过分设计”的大帽子,事先声明一下:只有当存储空间的分配/释放很是频繁而且确实产生了明显的影响,你才应该考虑环形缓冲区的使 用。不然的话,仍是老老实实用最基本、最简单的队列缓冲区吧。还有一点须要说明一下:本文所说起的“存储空间”,不只包括内存,还可能包括诸如硬盘之类的 存储介质。spa
★环形缓冲区 vs 队列缓冲区设计
◇外部接口类似索引
在介绍环形缓冲区以前,我们先来回顾一下普通的队列。普通的队列有一个写入端和一个读出端。队列为空的时候,读出端没法读取数据;当队列满(达到最大尺寸)时,写入端没法写入数据。接口
对于使用者来说,环形缓冲区和队列缓冲区是同样的。它也有一个写入端(用于push)和一个读出端(用于pop),也有缓冲区“满”和“空”的状态。因此,从队列缓冲区切换到环形缓冲区,对于使用者来讲能比较平滑地过渡。队列
◇内部结构迥异进程
虽然二者的对外接口差很少,可是内部结构和运做机制有很大差异。队列的内部结构此处就很少啰嗦了。重点介绍一下环形缓冲区的内部结构。内存
大伙儿能够把环形缓冲区的读出端(如下简称R)和写入端(如下简称W)想象成是两我的在体育场跑道上追逐(R追W)。当R追上W的时候,就是缓冲区为空;当W追上R的时候(W比R多跑一圈),就是缓冲区满。遍历
为了形象起见,去找来一张图并略做修改,以下:
从上图能够看出,环形缓冲区全部的push和pop操做都是在一个固定的存储空间内进行。而队列缓冲区在push的时候,可能会分配存储空间用于存储新元 素;在pop时,可能会释放废弃元素的存储空间。因此环形方式相比队列方式,少掉了对于缓冲区元素所用存储空间的分配、释放。这是环形缓冲区的一个主要优 势。
★环形缓冲区的实现
若是你手头已经有现成的环形缓冲区可供使用,而且你对环形缓冲区的内部实现不感兴趣,能够跳过这段。
◇数组方式 vs 链表方式
环形缓冲区的内部实现,便可基于数组(此处的数组,泛指连续存储空间)实现,也可基于链表实现。
数组在物理存储上是一维的连续线性结构,能够在初始化时,把存储空间一次性分配好,这是数组方式的优势。可是要使用数组来模拟环,你必须在逻辑上把数组的 头和尾相连。在顺序遍历数组时,对尾部元素(最后一个元素)要做一下特殊处理。访问尾部元素的下一个元素时,要从新回到头部元素(第0个元素)。以下图所 示:
使用链表的方式,正好和数组相反:链表省去了头尾相连的特殊处理。可是链表在初始化的时候比较繁琐,并且在有些场合(好比后面提到的跨进程的IPC)不太方便使用。
◇读写操做
环形缓冲区要维护两个索引,分别对应写入端(W)和读取端(R)。写入(push)的时候,先确保环没满,而后把数据复制到W所对应的元素,最后W指向下一个元素;读取(pop)的时候,先确保环没空,而后返回R对应的元素,最后R指向下一个元素。