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:

  1. We use std::atomic<uint64_t> instead of Go’s atomic.Uint64.
  2. We use std::thread instead of goroutines.
  3. We use std::vector<std::thread> to manage our threads instead of a WaitGroup.
  4. We use fetch_add() for atomic increments instead of Go’s Add().
  5. 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.

查看推荐产品

Comments powered by Disqus