#buffer #producer-consumer #element #single-consumer #synchronization #size #ping-pong

atomic_pingpong

轻量级 no_std ping-pong 缓冲区,使用 AtomicU8 进行同步

5 个版本

0.2.3 2023年6月12日
0.2.2 2023年4月25日
0.2.1 2023年4月20日
0.2.0 2023年4月19日
0.1.0 2023年4月17日

#774并发

每月 47 次下载

MIT 许可协议

20KB
234

ping-pong 缓冲区是一个包含两个元素的缓冲区,允许单个生产者和单个消费者同时访问。一个元素由生产者保留用于写入,另一个元素由消费者保留用于读取。当写入和读取完成后,这两个元素的角色的会交换(即被写入的元素将是下一个被读取的,而被读取的元素将被下一个覆盖)。这种方法避免了内存复制的需要,当元素大小较大时,可以提高性能。

此 ping-pong 缓冲区实现使用 AtomicU8 在生产者和消费者之间进行同步,从而实现线程安全和中断安全,同时开销最小。此实现支持 no_std 环境,但需要支持原子比较和交换的目标。


lib.rs:

轻量级 ping-pong 缓冲区,专为 no_std 目标设计。

ping-pong 缓冲区是一个包含两个元素的缓冲区,允许单个生产者和单个消费者同时访问。一个元素由生产者保留用于写入,另一个元素由消费者保留用于读取。当写入和读取完成后,这两个元素的角色的会交换(即被写入的元素将是下一个被读取的,而被读取的元素将被下一个覆盖)。这种方法避免了内存复制的需要,当元素大小较大时,可以提高性能。

ping-pong 缓冲区专门设计为允许同时读取和写入。然而,只有在没有进行读取或写入时,才能安全地交换两个元素的角色。用户负责确保读取和写入的时机允许这种交换发生。如果读取和写入被交错,使得一个总是在进行,那么缓冲区元素的角色的交换将不会发生,读者将继续读取旧值而不是新写入的值。

通过调用 Buffer<T>::read() 获取一个用于读取的引用,通过调用 Buffer<T>::write() 获取一个用于写入的可变引用。返回的类型是智能指针(分别是 Ref<T>RefMut<T>),当它们被丢弃时,会自动更新ping-pong缓冲区的状态。如果该类型的第一个引用尚未丢弃,尝试获取第二个读取或写入引用将失败。为了禁用自动引用管理,提供了一套不安全的访问函数:read_unchecked()write_unchecked()release_read()release_write()。这些函数提供了较低的运行时开销,但当然,在使用它们时需要小心。

通常,对 read()write() 的调用尽可能宽松:read() 在读取正在进行时失败,而 write() 在写入正在进行时失败。因此,根据 read()write() 调用的时机,某些写入的数据可能永远不会被读取,而其他写入的数据可能被读取多次。(这是ping-pong缓冲区与FIFO环形缓冲区之间的重要区别。)可以使用 read_once() 函数实现不同的行为,该函数仅在指向尚未读取的数据时返回 Ref<T>,以及 write_no_discard() 函数,该函数仅在缓冲区当前不包含未读取数据时返回 RefMut<T>

Buffer<T> 的内存占用是两个 T 加上额外的一个字节(一个 AtomicU8),用于同步生产者和消费者的访问。此实现的运行时开销小于获取或释放ping-pong缓冲区引用的约二十条指令(假设启用了函数内联)。然而,此crate只能在包含原子比较/交换指令集的目标上使用。

无运行时依赖