切换主题
通用 RingBuffer —— 第一版
连续到来的字节,生产速度和消费速度不完全一致时,数据到底怎么暂存?
1. 问题与背景
RingBuffer 就是在解决:生产者和消费者速度不完全同步的问题 最核心就两个位置:head 和 tail head → 下一个数据应该写到哪里 tail → 下一个数据应该从哪里读
例如: Index: 0 1 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ A │ B │ C │ D │ E │ F │ G │ H │ └───┴───┴───┴───┴───┴───┴───┴───┘ ↑ ↑ tail head
意味着:tail = 0 head = 3
下一次 Read → buffer[0] → A 下一次 Write → buffer[3]
代码里怎么实现 回绕? 最直观: head++; if (head >= capacity) { head = 0; }
也可以:head = (head + 1U) % capacity;
后一种更短,不过以后高频路径我们还会讨论:% 和2的幂容量优化。
怎么区分 Empty 和 Full? 方法 1:额外保存 count head; tail; count; count == 0 → Empty count == capacity → Full 虽然很直观,但是后面 ISR + Task 并发的时候,count 会成为一个麻烦: Producer修改 count Consumer也修改 count 两边共同写同一个变量,我们以后会分析 Race。
方法 2:额外保存 full Flag bool full可以解决但是多了分共享状态
方法 3:故意空出一个 Slot 也是我们采用的方法 规定: head == tail → 一定表示 Empty head下一步如果撞上tail → 认为 Full
因此容量为:N 实际最多存:N - 1个元素 它有个很好的点就是Producer 主要修改 head Consumer主要修改 tail 做: ISR Producer Task Consumer 时,这个设计特别值得研究。
虽然现在还不能简单宣布它线程安全,但数据所有权已经更干净。 我们从未来并发模型反推数据结构。