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 并发
16,256 每月下载量
用于 16 个 crates (6 个直接使用)
380KB
5K SLoC
thingbuf
"我在缓冲池中。我在 MPSC 通道中。我在 MPSC 通道和缓冲池的组合中。"
这是什么?
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