1个不稳定版本
0.1.0 | 2024年1月22日 |
---|
在音频中排名第412
32KB
579 行
spmcq
一个用于线程安全的单生产者、多消费者有界环形缓冲区(FIFO队列)的Rust库。
此库是针对实时音频应用编写的,其中典型场景可能如下
- 音频回调定期从一个线程上的麦克风接收新的音频数据缓冲区
- 一个单独的高优先级音频线程需要以最小延迟接收这些缓冲区。假设该线程以相同的频率读取,因此断开连接通常不太可能
- 一个低优先级的GUI线程也接收相同的音频数据并显示它。GUI线程运行得更少,可以容忍更大的延迟,但仍希望在必要时以旧数据批次的形式接收整个音频信号。由于GUI未重新绘制而导致的断开连接是可以容忍的,甚至可能是预期的。
功能
- 固定大小容量,构建后没有额外的堆分配
- 多个读取器
- 写入者可以在不发生错误或额外阻塞的情况下超越读取器,并且读取器可以检测此场景并可能跳过
- 低延迟和低同步开销。读取和写入都由简单的自旋锁和单个项目内存复制组成。
基本用法
高级API与std::sync::mpsc::sync_channel
的API类似。选择固定大小容量,并使用该大小调用ring_buffer
以接收一个Reader<T>
和一个Writer<T>
。两者都可以传递到不同的线程,并且Reader<T>
可以被克隆。Writer::write
将新值推送到缓冲区。Reader::read
尝试读取其序列中的下一个值。
let (mut reader, mut writer) = ring_buffer::<usize>(256);
std::thread::spawn(move ||{
for i in 1000 {
writer.write(i);
std::thread::sleep(Duration::from_millis(1))
}
});
std::thread::spawn(move ||{
loop {
match reader.read() {
ReadResult::Ok(i) => println!("Received {}", i),
ReadResult::Dropout(i) => println!("Received {} but lost some values", i),
ReadResult::Empty(i) => println("No new data"),
}
std::thread::sleep(Duration::from_millis(1));
}
});
如果读者完全跟上了作者,read()
将返回 ReadResult::Empty
直到有更多内容写入。如果读者位于队列的前端和后端之间,read()
将返回包含其下一个值的 ReadResult::Ok(_)
。否则,如果写入者完全超越了读者,其 read()
方法将返回 ReadResult::Dropout(_)
,表示读者自上次读取以来至少落后了一圈,但仍返回当前圈的价值。
为了将读者跳到队列的前端,调用 Reader::skip_ahead()
。下一次读取将始终返回 ReadResult::Dropout(_)
,但如果可以容忍丢失的值,则可以通过这种方式减少累积的延迟。
存储的数据类型 T
必须是 Copy
。这个约束允许最小化读者在每次项上保持读取锁的时间,因为锁必须保持足够长的时间来执行项的 memcpy。
待办事项
- 是否允许读者检测到写入者丢失时的挂起情况?