Atomic (thread-safe) counters for Rust.
This crate contains an
that can safely be shared across threads.
This crate provides two implementations:
ConsistentCounterwhich provides the same interface but is sequentially consistent. Use this counter if the order of update from multiple threads is important.
Sequentially Consistentmemory ordering.
Both implementations are lock-free. Both are a very thin layer over
which is more powerful but might be harder to use correctly.
If you are just collecting metrics, the
RelaxedCounteris probably right choice.
If you are generating IDs, but don't make strong assumptions (like allocating memory based on the ID count),
RelaxedCounteris probably the right choice.
If you are generating multiple IDs where you maintain an ordering invariant (e.g. ID
ais always greater than ID
b), you need "Sequential Consistency" and thus need to use
ConsistentCounter. The same is true for all use cases where the ordering of incrementing the counter is important.
Note that in both implementations, no count is lost and all operations are atomic. The difference is only in how the order of operations are observed by different threads.
a is 5 and
b is 4. You always want to maintain
a > b.
Thread 1 executes this code:
Thread 2 gets counts:
let a_local = a.get(); let b_local = b.get();
What are the values for
b_local? That depends on the order
in which thread 1 and 2 have run:
a_localcould still be 5 and
b_localis still be 4 (e.g. if thread 2 ran before thread 1)
a_localcould be increment to 6 while
b_localis still at 4 (e.g. if thread 1 and 2 ran in parallel)
a_localcould be increment to 6 and
b_localbe incremented to 5 (e.g. if thread 2 ran after thread 1).
- Additionally, if at least one counter is a
RelaxedCounter, we cannot make assumption on the order of
b.inc(). Thus, in this case thread 2 can also observe
a_localto be 5 (not incremented yet) but
b_localto be incremented to 5, breaking the invariant
a > b. Note that if thread 2 (or any other thread)
get()the counts again, at some point they will observe both values to be incremented. No operations will be lost. It is only the ordering of the operations that cannot be assumed if
So in order to maintain invariants such as
a > b across multiple threads,
Provides an atomic counter trait that can be shared across threads.