#mpsc #ring-buffer #channel #queue #lock-free-queue #lock-free #pool

无 std thingbuf

我在缓冲池。我在 MPSC 通道中。我在 MPSC 通道和缓冲池的组合中。

7 个版本

0.1.6 2024年4月18日
0.1.5 2024年4月6日
0.1.4 2023年5月1日
0.1.3 2022年5月13日
0.1.1 2022年3月16日

#30 in 并发

Download history 3650/week @ 2024-05-03 3157/week @ 2024-05-10 3854/week @ 2024-05-17 3949/week @ 2024-05-24 4056/week @ 2024-05-31 3428/week @ 2024-06-07 3856/week @ 2024-06-14 3981/week @ 2024-06-21 3398/week @ 2024-06-28 3975/week @ 2024-07-05 4480/week @ 2024-07-12 4659/week @ 2024-07-19 4771/week @ 2024-07-26 3562/week @ 2024-08-02 4418/week @ 2024-08-09 2595/week @ 2024-08-16

16,256 每月下载量
用于 16 个 crates (6 个直接使用)

MIT 许可证

380KB
5K SLoC

thingbuf

"我在缓冲池中。我在 MPSC 通道中。我在 MPSC 通道和缓冲池的组合中。"

crates.io Documentation Documentation (HEAD) MIT licensed Test Status Sponsor @hawkw on GitHub Sponsors

这是什么?

thingbuf 是一个无锁的基于数组的并发环形缓冲区,允许通过引用访问缓冲区中的槽位。它也是使用环形缓冲区实现的 异步阻塞 有界 MPSC 通道。

何时使用它?

  • 如果您需要一个高吞吐量的有界 MPSC 通道,它在通道创建时仅分配。一些 MPSC 通道具有很好的吞吐量。其他一些 MPSC 通道不会为每个等待者分配内存。 thingbuf::mpsc 具有这些特点。 thingbuf::mpsc 是大多数用例中通用 MPSC 通道的竞争性选择。

    提供了异步和阻塞的 MPSC 通道,因此 thingbuf 可以替代异步通道,如 futures::channel::mpsc 阻塞通道,如 std::sync::mpsc::sync_channel

  • 如果你无法分配因为你正在处理嵌入式系统或其他裸机软件,需要使用#![no_std]来构建,那么 Thingbuf 提供了一个静态分配的 MPSC 通道StaticChannel和一个静态分配的无锁队列StaticThingBuf。这些可以在一个static初始化器中放置,并且可以在不要求任何运行时分配的情况下使用。

  • 你希望在启用或禁用 std 的情况下都能使用相同的 MPSC 通道。无论是否启用 "std" 功能标志,Thingbuf 的异步 MPSC 通道都提供了一个相同的 API 和功能集。如果你正在编写一个需要条件支持 #![no_std] 的库,并且你需要一个异步 MPSC 通道,那么在两种情况下使用 thingbuf::mpsc 可能会更简单,而不是在单独的 std#![no_std] 通道实现之间切换。

何时不应使用它?

同样重要的是讨论在什么情况下不应该使用 thingbuf。以下是一些你可能需要考虑其他选项的情况

  • 你需要一个非常高且实际上很少会接近的界限。如果你想为有界 MPSC 通道设置一个非常高的界限,并且通道通常永远不会接近这种满载状态,那么 thingbuf::mpsc 可能 不是 最佳选择。

    Thingbuf 的通道在构建时会分配一个长度等于容量的数组。这通过避免额外的分配来提高性能,但如果你需要设置非常高的界限,你可能更喜欢只在需要时为消息分配内存的通道实现(例如 tokio::sync::mpsc)。

  • 你需要一个带有 select 操作的阻塞通道。我可能不会实现它。如果你提出一个 PR,我 可能 会接受。

    如果你需要一个具有此类功能的前同步通道,那么 crossbeam-channel 可能是一个不错的选择。

  • 你想要一个无界通道。我不会编写一个无界通道。无界通道是邪恶的。

术语

此软件包的 API 和文档在 "队列" 和 "通道" 术语之间做出了区分。术语 队列 将指代通用的 队列抽象数据类型 - 任何先进先出(FIFO)数据结构都是队列。

术语 通道 将指代并发队列的一个子类型,它同时作为同步原语。通道是一个队列,可以在多个线程或异步任务之间共享,并允许这些线程或任务等待元素被添加到或从队列中移除。

在Rust标准库中,std::collections::VecDeque 类型是一个非通道的队列示例:它是一种先进先出的数据结构,但不能由多个线程或任务同时进行入队和出队操作。相比之下,std::sync::mpsc 模块中的类型提供了一个通道的原型示例,因为它们作为跨线程通信的同步原语。

使用方法

要开始使用 thingbuf,请在您的 Cargo.toml 中添加以下内容

[dependencies]
thingbuf = "0.1"

默认情况下,thingbuf 依赖于Rust标准库,以便实现同步(阻塞)通道等API。在 #![no_std] 项目中,必须禁用 std 特性标志

[dependencies]
thingbuf = { version = "0.1", default-features = false }

禁用 std 特性后,thingbuf 将仅依赖于 libcore。这意味着不需要动态内存分配的API将不可用。如果启用了 static 特性标志,则对于没有内存分配器的代码,将可用静态分配的 通道队列

[dependencies]
thingbuf = { version = "0.1", default-features = false, features = ["static"] }

然而,如果有一个内存分配器 可用,则 #![no_std] 代码也可以启用 alloc 特性标志,以便依赖于 liballoc

[dependencies]
thingbuf = { version = "0.1", default-features = false, features = ["alloc"] }

包特性标志

  • std默认启用):启用需要Rust标准库的功能,例如同步(阻塞)通道。这隐式启用了“alloc”特性标志。
  • alloc:启用需要 liballoc (但不是 libstd)的功能。这启用了 thingbuf 队列和异步通道,其中通道的大小在运行时确定。
  • static默认禁用,需要Rust 1.59+):启用静态(基于const-generic的) thingbuf 队列和通道。当队列或通道的大小在编译时已知时,可以不使用动态内存分配来使用这些。

编译器支持

thingbuf 是针对最新的稳定版本构建的。最低支持的版本是Rust 1.57。当前 thingbuf 版本不保证在低于最低支持版本的Rust版本上构建。

某些特性标志可能需要更新的Rust版本。例如,“static”特性标志需要Rust 1.60+。

常见问题解答

  • Q:你为什么要这样做?

    A:为了 tracing,我想要能够将格式化的日志行发送到专门的工作线程,该线程将它们写入文件。目前我们使用 crossbeam-channel 来这样做。然而,这有一个不利的缺点,即我们必须分配 String,将它们通过通道发送到写入器,然后立即丢弃它们。如果能重用这些分配将会更好。因此...StringBuf

  • Q:它是无锁的吗?

    A:极其。

  • Q:为什么只有有限制的变体?

    A: 因为无界队列就像是恶魔。

  • Q: 这难道不是巨大的内存泄露吗?

    A: 如果你使用不当,是的。

  • Q: 为什么叫这个名字?

    A: 最初,我把它想象成一种环形缓冲区,所以(作为“ringbuf”的双关语),我称之为“stringbuf”。后来,我发现这不仅仅是字符串可以做到。事实上,它可以推广到任意...事物。所以,“thingbuf”。

依赖项

~0.3–5.5MB
~20K SLoC