#fifo-queue #ring-buffer #queue #fifo #ipc #spmc

spmcq

一个用于线程安全的单生产者、多消费者有界环形缓冲区(FIFO队列)的Rust库。

1个不稳定版本

0.1.0 2024年1月22日

音频中排名第412

MIT许可证

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。

待办事项

  • 是否允许读者检测到写入者丢失时的挂起情况?

无运行时依赖