Expand description
§AtomicCounter
Atomic (thread-safe) counters for Rust.
This crate contains an AtomicCounter trait
that can safely be shared across threads.
This crate provides two implementations:
-
RelaxedCounterwhich is suitable for e.g. collecting metrics or generate IDs, but which does not provide “Sequential Consistency”.RelaxedCounterusesRelaxedmemory ordering. -
ConsistentCounterwhich provides the same interface but is sequentially consistent. Use this counter if the order of update from multiple threads is important.ConsistentCounterusesSequentially Consistentmemory ordering.
Both implementations are lock-free. Both are a very thin layer over
AtomicUsize
which is more powerful but might be harder to use correctly.
§Which counter to use
-
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 IDb), you need “Sequential Consistency” and thus need to useConsistentCounter. The same is true for all use cases where the ordering of incrementing the counter is important.
§No updates are lost - It’s just about the ordering!
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.
§Example:
Assume a is 5 and b is 4. You always want to maintain a > b.
Thread 1 executes this code:
a.inc();
b.inc();Thread 2 gets counts:
let a_local = a.get();
let b_local = b.get();What are the values for a_local and b_local? That depends on the order
in which thread 1 and 2 have run:
a_localcould still be 5 andb_localis still be 4 (e.g. if thread 2 ran before thread 1)a_localcould be increment to 6 whileb_localis still at 4 (e.g. if thread 1 and 2 ran in parallel)a_localcould be increment to 6 andb_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 ofa.inc()andb.inc(). Thus, in this case thread 2 can also observea_localto be 5 (not incremented yet) butb_localto be incremented to 5, breaking the invarianta > 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 ifOrderingisRelaxed.
So in order to maintain invariants such as a > b across multiple threads,
use ConsistentCounter.
Structs§
- Consistent
Counter - Implementation of
AtomicCounterthat usesSequentially Consistentmemory ordering. - Relaxed
Counter - Implementation of
AtomicCounterthat usesRelaxedmemory ordering.
Traits§
- Atomic
Counter - Provides an atomic counter trait that can be shared across threads.