fifo之读写指针
定义
写指针:生产者。它的职责是“向哪里写入新数据”。它总是指向下一个可以写入数据的位置。写完一个数据后,它就向前移动一步,为下一次写入做准备。
读指针:消费者。它的职责是“从哪里读取下一个数据”。它总是指向下一个将要被读取的数据的位置。读完一个数据后,它就向前移动一步,准备读取下一个。
缓冲区为空:当读指针和写指针指向同一个位置时,意味着“读指针已经追上了写指针”,所有数据都已被读取,没有新数据可读。这是“消费者”需要等待的状态。
缓冲区为满:当写指针绕了一圈,快要追上读指针时(具体实现上通常会留一个空格子作为判断标志),意味着所有格子都写满了数据,但没有被及时读取。此时“生产者”需要停止写入,否则会覆盖未读数据。这是“生产者”需要等待的状态。
为什么要用两个指针?(解决了什么问题)
使用读/写指针的核心目的是为了实现 生产者-消费者模型 下的异步处理和解耦。
速度匹配:生产数据(写入)和消费数据(读取)的速度通常是不一致的。有时生产快,有时消费快。这个缓冲区就像一个“水库”,在洪峰时蓄水(生产快时存数据),在枯水时放水(消费快时提供数据),平滑了上下游的流量。
避免等待:如果没有缓冲区,生产者必须等待消费者处理完当前数据才能提供下一个,反之亦然,效率极低。有了指针管理的缓冲区,双方可以连续工作,只要缓冲区不为空或不为满即可。
线程安全:在多线程编程中,一个线程负责写入(操作写指针),另一个线程负责读取(操作读指针)。通过正确地管理这两个指针,可以高效地在线程间安全地传递数据,而无需复杂的锁机制(在某些无锁编程中)。
场景设定
假设我们有一个非常小的环形缓冲区,其长度为 6(即拥有 6 个存储格子,索引从 0 到 5)。
根据预留空间法,我们实际只能使用 5 个格子来存储数据,有 1 个格子被牺牲用于判断“满”状态。
操作序列 | Read Ptr | Write Ptr | 缓冲区内容 (0-5) | 状态 |
---|---|---|---|---|
1. 初始 | 0 | 0 | [ _, _, _, _, _, _ ] | 空 |
2. 写 ‘A’ | 0 | 1 | [ A, _, _, _, _, _ ] | 有数据 |
3. 写 ‘B’,’C’,’D’,’E’ | 0 | 5 | [ A, B, C, D, E, _ ] | 有数据 |
4. 尝试写 ‘F’ | 0 | 5 | [ A, B, C, D, E, _ ] | 满 (拒绝写入) |
5. 读 ‘A’ | 1 | 5 | [ _, B, C, D, E, _ ] | 有数据 |
6. 读 ‘B’, ‘C’ | 3 | 5 | [ _, _, _, D, E, _ ] | 有数据 |
7. 写 ‘F’, ‘G’ | 3 | 1 | [ G, _, _, D, E, F ] | 有数据 (环绕) |
8. 读 ‘D’,’E’,’F’,’G’ | 1 | 1 | [ _, _, _, _, _, _ ] | 空 |