Atomic Counters in C++
Our example demonstrates the use of atomic counters in C++. While C++ doesn’t have built-in goroutines, we’ll use threads to showcase concurrent operations on a shared counter.
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
int main() {
// We'll use an atomic integer type to represent our
// (always-positive) counter.
std::atomic<uint64_t> ops(0);
// We'll start 50 threads that each increment the
// counter exactly 1000 times.
std::vector<std::thread> threads;
for (int i = 0; i < 50; i++) {
threads.emplace_back([&ops]() {
for (int c = 0; c < 1000; c++) {
// To atomically increment the counter we use fetch_add.
ops.fetch_add(1, std::memory_order_relaxed);
}
});
}
// Wait until all the threads are done.
for (auto& t : threads) {
t.join();
}
// Here no threads are writing to 'ops', but using
// load() it's safe to atomically read a value even while
// other threads are (atomically) updating it.
std::cout << "ops: " << ops.load(std::memory_order_relaxed) << std::endl;
return 0;
}
To compile and run this program:
$ g++ -std=c++11 -pthread atomic_counters.cpp -o atomic_counters
$ ./atomic_counters
ops: 50000
We expect to get exactly 50,000 operations. Had we used a non-atomic integer and incremented it with ops++
, we’d likely get a different number, changing between runs, because the threads would interfere with each other. Moreover, we’d get undefined behavior due to data races.
In this C++ version:
- We use
std::atomic<uint64_t>
instead of Go’satomic.Uint64
. - We use
std::thread
instead of goroutines. - We use
std::vector<std::thread>
to manage our threads instead of aWaitGroup
. - We use
fetch_add()
for atomic increments instead of Go’sAdd()
. - We use
load()
to safely read the final value.
Note that C++ atomic operations allow specifying memory ordering. In this example, we use std::memory_order_relaxed
for simplicity, but in real-world scenarios, you might need stronger ordering guarantees depending on your specific requirements.
Next, we’ll look at mutexes, another tool for managing state in concurrent programs.