#ring-buffer #lock-free #fifo #spsc #producer-consumer #memory-access

无 std ringbuf

带有对内部数据直接访问的无锁 SPSC FIFO 环形缓冲区

31 个版本

新版本 0.4.4 2024 年 8 月 20 日
0.4.1 2024 年 5 月 15 日
0.4.0-rc.42024 年 3 月 3 日
0.4.0-rc.22023 年 8 月 31 日
0.1.4 2019 年 1 月 21 日

#16 in 并发

Download history 29834/week @ 2024-05-03 33786/week @ 2024-05-10 41661/week @ 2024-05-17 34621/week @ 2024-05-24 32851/week @ 2024-05-31 39767/week @ 2024-06-07 35149/week @ 2024-06-14 33197/week @ 2024-06-21 31011/week @ 2024-06-28 33726/week @ 2024-07-05 26546/week @ 2024-07-12 33953/week @ 2024-07-19 29038/week @ 2024-07-26 31497/week @ 2024-08-02 36094/week @ 2024-08-09 30668/week @ 2024-08-16

133,986 每月下载量
138 个 Crates 中使用 (61 个直接使用)

MIT/Apache

130KB
3K SLoC

ringbuf

Crates.io Docs.rs Github Actions Gitlab CI License

带有对内部数据直接访问的无锁 SPSC FIFO 环形缓冲区。

特性

  • 无锁操作 - 它们立即成功或失败,没有阻塞或等待。
  • 任意项目类型(不仅限于 Copy)。
  • 项目可以逐个或一次性插入和移除。
  • 线程安全直接访问内部环形缓冲区内存。
  • ReadWrite 实现。
  • 支持覆盖插入。
  • 不同类型的缓冲区和底层存储。
  • 可以在不使用 std 的情况下使用,甚至可以在不使用 alloc 的情况下使用(仅使用静态分配的内存)。
  • 异步和阻塞版本(请参阅 本节)。

用法

首先需要创建环形缓冲区本身。建议使用 HeapRb,但您可以选择 其他类型

创建环形缓冲区后,它可能被拆分为一对 ProducerConsumer。生产者用于向环形缓冲区插入项目,消费者用于从中移除项目。

类型

提供了几种类型的环形缓冲区

  • LocalRb。仅用于单线程。
  • SharedRb。可以在线程之间共享。其常用实例
    • HeapRb。内容存储在动态内存中。 推荐在大多数情况下使用。
    • StaticRb。内容可以存储在静态分配的内存中。

您还可以提供自己的泛型参数。

性能

SharedRb 需要在 CPU 核之间同步 CPU 缓存。这种同步会产生一些开销。为了避免多次不必要的同步,您可以使用一次操作多个项目的方法(如 push_slice/push_iterpop_slice/pop_iter 等)或者您可以将生产者或消费者 freeze 并手动同步线程(参见 frozen 模块中的项目)。

对于单线程使用,建议使用 LocalRb,因为它比 SharedRb 快一些,因为没有 CPU 缓存同步。

示例

简单

use ringbuf::{traits::*, HeapRb};

let rb = HeapRb::<i32>::new(2);
let (mut prod, mut cons) = rb.split();

prod.try_push(0).unwrap();
prod.try_push(1).unwrap();
assert_eq!(prod.try_push(2), Err(2));

assert_eq!(cons.try_pop(), Some(0));

prod.try_push(2).unwrap();

assert_eq!(cons.try_pop(), Some(1));
assert_eq!(cons.try_pop(), Some(2));
assert_eq!(cons.try_pop(), None);

无堆

use ringbuf::{traits::*, StaticRb};

const RB_SIZE: usize = 1;
let mut rb = StaticRb::<i32, RB_SIZE>::default();
let (mut prod, mut cons) = rb.split_ref();

assert_eq!(prod.try_push(123), Ok(()));
assert_eq!(prod.try_push(321), Err(321));

assert_eq!(cons.try_pop(), Some(123));
assert_eq!(cons.try_pop(), None);

覆盖

环形缓冲区可以在覆盖模式下使用,当缓冲区满时,插入会覆盖最新元素。

use ringbuf::{traits::*, HeapRb};

let mut rb = HeapRb::<i32>::new(2);

assert_eq!(rb.push_overwrite(0), None);
assert_eq!(rb.push_overwrite(1), None);
assert_eq!(rb.push_overwrite(2), Some(0));

assert_eq!(rb.try_pop(), Some(1));
assert_eq!(rb.try_pop(), Some(2));
assert_eq!(rb.try_pop(), None);

请注意,push_overwrite 需要独占访问环形缓冲区,因此为了并行执行,您需要使用互斥锁或其他锁来保护环形缓冲区。

衍生crate

许可证

在以下任一许可证下授权:

任选其一。

贡献

除非您明确声明,否则根据 Apache-2.0 许可证定义,您有意提交的任何贡献,均应如上双许可,不附加任何额外条款或条件。

依赖关系

~110KB