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
。