You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
obsidian-sync/日常学习/数据结构/RingBuffer(环形缓冲队列).md

30 lines
4.3 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

**RingBuffer**, 这种结构实现起来只需要几行代码即可但使用场景却很广泛比如在Linux内核中网络数据包的缓存系统日志的存储等多处使用过该结构。同时它也被广泛的应用于异步通信以及嵌入式设备中提供高效的数据缓存读写操作。
# 一、原理
RingBufferr实现比较简单基本上只需要一个数组结构外加两个用于存储位置信息的变量即可。其中的数组采用固定大小容量便于重用内存不会出现动态内存不断分配和销毁的情况这对于一些GC类编程语言来说大幅减少了内存管理的成本。
下面就是一个典型的RingBuffer图例包含一个容量大小为6的数组以及一个读和写指针。其中Write和Read用于管理读写的位置序列读写开始后不断增加该值来定位下一次的读写位置。实际上由3个参数控制读指针、写指针、容量
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA.png]]
每次写数据在Write对应位置写入新值并向前移动对应的Write指针位置如果遇到指针已经处于尾部则移动到最开始位置形成一个环形 类似于双向链表。
每次读取数据在Read位置读取当前值并移动Read位置同样如果遇到已经到达尾部则返回到最开始的初始位置。
整个数据流的读写过程就是通过不断的操作Write和Read来实现数据的高效处理。
# 二、读写操作实例
通过一个简单的实例来介绍如何操作RingBuffer来管理数据 首先初始化一个空的数组并设置Read=0和Write=0 图例中黄色代表已写入的数据,绿色代表已读取的数据,红色代表异常情况:
1 写入三个元素分别是123, 这时候读指针位置不变写指针移动三个位置到索引为3的位置数组索引位置从0开始
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 1.png]]
2读取一个元素读指针移动一个位置写指针不变获取数据值1
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 2.png]]
3继续写入四个元素分别是4567 其中456分别放入剩余的数组空缺中但是7 由于已经没有位置可写则从0开始覆盖原有写入1的位置。注意这里我们没有设置write=0,而是直接在原Write值上继续加1我们取模size即可获得新的写入位置取模后重新返回头部
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 3.png]]
4当我们再次写入两个值89的时候由于与上一轮的Read发生了交叉为了保证前后读写的顺序性**我们需要同时移动读指针的位置,使得读位置总是指向最旧的数据**。
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 4.png]]
5这时候如果读取两个数据则读指针只需要按照当前序列向前移动两个位置即可分别获得值45 代表了最早的数据项,假如上面我们没有移动读指针,则读取的可能会是最新数据。
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 5.png]]
6如果我们这时候读取速度加快假如读取5个值可成功读取6789当读取到4值的时候由于此时读写位置重叠读数据不能超过写数据的位置否则重复读取的问题无法进一步读取数据。退出读取流程。
![[aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FSDlId2ljaHd6NVlpYVZoeWxzcWhWS05NaWJ0SkFpYlR1T2VYNFB5aWJQMU03TmRRd292TVZiVUxjZjVtckhqRHJ4dk1FbFdmd3dGaWE2TjZwTzZ4M0V6dXVVdy82NDA 6.png]]