#spmc #同步 #多线程 #无等待 #非阻塞 #共享内存 #输入输出

spmc_buffer

适用于多消费者场景的三缓冲扩展,用于在多个线程之间共享频繁更新的数据

9个不稳定版本 (4个破坏性更新)

使用旧的Rust 2015

0.5.1 2018年2月11日
0.4.2 2017年11月13日
0.4.1 2017年7月25日
0.3.0 2017年3月28日

#15#spmc

Download history 5/week @ 2024-04-01

每月下载量 98次

MPL-2.0 许可证

51KB
570

SPMC Buffer:多消费者使用的三缓冲

On crates.io On docs.rs Build status

这是什么?

这是我之前关于三缓冲工作的扩展,它支持从多个消费者读取,以牺牲一些额外的内存和CPU开销为代价。您可能会发现它在以下类型的线程同步问题中很有用:

  • 有一个生产者线程和几个消费者线程
  • 生产者希望定期更新共享内存值
  • 消费者希望随时访问生产者的最新更新

它目前的使用方式如下

// Create an SPMC buffer of any Clone type
use spmc_buffer::SPMCBuffer;
let buf = SPMCBuffer::new(2, 1.0);

// Split it into an input and output interface
let (mut buf_input, mut buf_output) = buf.split();

// Create as many extra output interfaces as needed
let mut buf_output2 = buf_output.clone();

// The producer can move a value into the buffer at any time
buf_input.write(4.2);

// A consumer can access the latest value from the producer at any time
let mut latest_value_ref = buf_output.read();
assert_eq!(*latest_value_ref, 4.2);
let latest_value_ref2 = buf_output2.read();
assert_eq!(*latest_value_ref2, 4.2);

给我详细说明!它与替代方案相比如何?

与三缓冲相比,SPMC缓冲...

  • 支持多个消费者(这是关键!)
  • 在单消费者情况下消耗更多的CPU时间和内存
  • 对于写者来说,不总是无等待的。可以提供保证,但许多读者的内存成本相当高。

简而言之,SPMC缓冲是在以下场景中您所需要的:共享内存位置由单个写者频繁更新,由多个只想要最新版本的读者读取,并且您可以省出一些RAM。

  • 如果您需要多个生产者,请另寻它处
  • 如果您只需要一个消费者,请使用三缓冲
  • 如果您无法忍受RAM开销或希望就地更新数据,请尝试Mutex(或可能是一个RWLock)
  • 如果共享值很少更新(例如每秒一次),请尝试RCU
  • 如果消费者必须获取每个更新,请尝试消息队列

我如何知道您的无锁代码是有效的?

当然是通过运行测试!但不幸的是,目前比我想的要困难。

首先,我们有顺序测试,这些测试非常彻底,但显然没有检查无锁/同步部分。您可以这样运行它们:

$ cargo test --release

然后我们有并发测试,其中我们启动并发读取器和写者线程,并检查读取者永远不会观察到不一致的缓冲区状态。这些测试更重要,但运行它们也更困难,因为必须首先检查一些假设:

  • 测试主机必须至少有3个物理CPU核心才能测试所有可能的竞争条件
  • 背景中没有其他代码应该消耗CPU。包括其他测试。
  • 一些测试具有时序相关的行为,可能需要根据您的特定系统手动调整休眠时间。

考虑到这一点以及相对较长的运行时间(约10秒),这些测试默认情况下会被忽略。

最后,我们有基准测试,允许您测试代码在您的机器上的性能。由于 cargo bench 尚未进入稳定 Rust,这些基准测试伪装成测试,这使得它们运行起来有些不便。对此带来的不便,我表示歉意。

要运行并发测试和基准测试,请确保后台没有占用 CPU,并执行以下操作:

$ cargo test --release -- --ignored --test-threads=1

以下是解读基准测试结果指南

  • clean_read 测量数据未发生变化时的三缓冲区读取时间。它应该非常快(几个 CPU 时钟周期)。
  • write 测量在没有人读取时写入三缓冲区所需的时间。
  • write_and_dirty_read 在之前进行写入操作后立即进行顺序读取。为了获得脏读性能,从该结果中减去写入时间。写入和脏读应该花费可比较的时间。
  • concurrent_write 测量在读者连续读取时的写入性能。预期性能会有明显下降:无锁技术可以帮助减少竞争,但并非万能。
  • concurrent_read 测量在写入者连续写入时的读取性能。同样,预期会有显著的影响。

在我的笔记本电脑 CPU(Intel Core i7-4720HQ)上,典型结果如下:

  • 写入:12 ns
  • 干净读取:1.3 ns
  • 脏读取:17 ns
  • 并发写入:100 ns
  • 并发读取:38 ns

许可证

本软件包在 MPLv2 许可证的条款下分发。有关详细信息,请参阅 LICENSE 文件。

也可以协商更宽松的许可证(Apache、MIT、BSD...),以换取财务捐助。如需详细信息,请联系 knights_of_ni AT gmx DOTCOM。

依赖关系

~140KB