3 个版本 (稳定)
使用旧的 Rust 2015
| 1.0.1 | 2018 年 9 月 27 日 |
|---|---|
| 1.0.0 | 2018 年 9 月 26 日 |
| 0.1.0 | 2017 年 9 月 8 日 |
#469 在 并发 中
52,231 每月下载量
用于 18 个 crate (15 个直接使用)
18KB
204 行
AtomicCounter
Rust 的原子(线程安全)计数器。
这个 crate 包含了一个可以安全地在线程之间共享的 AtomicCounter trait。
这个 crate 提供了两个实现
-
RelaxedCounter,适用于例如收集指标或生成 ID,但不提供 "顺序一致性"。RelaxedCounter使用Relaxed内存顺序。 -
ConsistentCounter,提供相同的接口,但具有顺序一致性。如果多个线程更新顺序很重要,请使用此计数器。ConsistentCounter使用Sequentially Consistent内存顺序。
两个实现都是无锁的。两者都是基于 AtomicUsize 的一层薄薄的封装,它更强大,但可能更难正确使用。
使用哪个计数器
-
如果您只是收集指标,那么
RelaxedCounter可能是正确的选择。 -
如果您正在生成 ID,但没有做出强烈的假设(例如,根据 ID 计数分配内存),则
RelaxedCounter可能是正确的选择。 -
如果您正在生成多个 ID,并保持顺序不变性(例如,ID
a总是大于 IDb),则需要“顺序一致性”,因此需要使用ConsistentCounter。同样适用于所有需要计数器增加顺序的用例。
不会丢失更新 - 只关乎顺序!
请注意,在这两个实现中,不会丢失计数,所有操作都是原子的。差异仅在于不同线程观察到的操作顺序。
示例
假设 a 为 5,b 为 4。你总是想保持 a > b。
线程 1 执行此代码
a.inc();
b.inc();
线程 2 获取计数
let a_local = a.get();
let b_local = b.get();
a_local 和 b_local 的值是多少?这取决于线程 1 和 2 的运行顺序。
- 如果线程 2 在线程 1 之前运行,
a_local仍可能是 5,而b_local仍可能是 4。 - 如果线程 1 和 2 并行运行,
a_local可能为 6,而b_local仍为 4。 - 如果线程 2 在线程 1 之后运行,
a_local可能为 6,而b_local可能为 5。 - 此外,如果至少有一个计数器是
RelaxedCounter,我们无法假设a.inc()和b.inc()的顺序。因此,在这种情况下,线程 2 可能会观察到a_local为 5(尚未增加)而b_local增加到 5,违反了不变量a > b。请注意,如果线程 2(或任何其他线程)再次get()计数,它们将在某个时刻观察到两个值都增加了。不会有操作丢失。只是在Ordering为Relaxed时,无法假设操作的 顺序。
因此,为了在多个线程之间保持如 a > b 这样的不变量,请使用 ConsistentCounter。