C++
std::atomic
concurrency
multithreading
thread safety

What exactly is stdatomic?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

The std::atomic template in C++ is an abstraction introduced with C++11 to manage atomic operations. Atomic operations are operations that are performed as a single, indivisible unit. They play a crucial role in multi-threaded and concurrent programming, ensuring that shared data is manipulated safely without the risk of race conditions.

Understanding std::atomic

What is std::atomic?

  • Atomicity: Operations on std::atomic types are guaranteed to be atomic, ensuring that when one thread changes the value of an atomic variable, other threads see either the value before the change or the value after the change, but never a partially updated value.
  • Lock-free Programming: std::atomic allows you to write lock-free and wait-free code, which is essential for high-performance applications because it reduces the overhead associated with acquiring and releasing locks.

Key Features

  • Atomic Operations: Support for atomic load (load), store (store), exchange (exchange), and compare-and-swap (compare_exchange_weak, compare_exchange_strong) operations.
  • Arithmetic Support: Provides atomic arithmetic operations such as fetch-and-add (fetch_add) and fetch-and-subtract (fetch_sub).
  • Memory Order: Support for different memory order constraints like memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, and memory_order_seq_cst.
  • Thread-Safety: Ensures operations on the atomic variables are safe across multiple threads without explicit synchronization mechanisms.

Basic Operations

Here’s a simple example to demystify how std::atomic works:

cpp
1#include <iostream>
2#include <atomic>
3#include <thread>
4
5std::atomic<int> counter(0);
6
7void incrementCounter() {
8    for (int i = 0; i < 1000; ++i) {
9        counter.fetch_add(1, std::memory_order_relaxed);
10    }
11}
12
13int main() {
14    std::thread t1(incrementCounter);
15    std::thread t2(incrementCounter);
16
17    t1.join();
18    t2.join();
19
20    std::cout << "Final counter value: " << counter.load() << std::endl; // Should print 2000
21    return 0;
22}

In the example above, the counter is incremented atomically by two threads simultaneously. Given the atomic nature of the fetch_add operation, there’s no race condition, and the final value of counter is reliably 2000.

Memory Order Models

The memory order model is an important concept associated with atomic operations. Let's explore the different memory orders you can specify and what they imply:

Memory OrderDescription
memory_order_relaxedNo synchronization or ordering constraints, just atomicity.
memory_order_consumeEnsures that subsequent operations that depend on the result of this operation are not moved before this operation.
memory_order_acquireNo reads or writes in the current thread can be reordered before this load.
memory_order_releaseNo reads or writes in the current thread can be reordered after this store.
memory_order_acq_relCombines acquire and release semantics.
memory_order_seq_cstProvides a single total order of all sequentially-consistent operations.

These memory orders provide a way to balance between stronger synchronization guarantees and performance. Understanding them is essential to optimize concurrency and achieve desired correctness in multi-threaded programs.

Advanced Features

Compare-and-Swap

The compare-and-swap operation is one of the most powerful operations provided by std::atomic. It allows an atomic comparison and update:

cpp
1int expected = 0;
2if (counter.compare_exchange_weak(expected, 1)) {
3    std::cout << "Counter updated from 0 to 1" << std::endl;
4} else {
5    std::cout << "Update failed, counter was not 0 initially" << std::endl;
6}

The compare_exchange_weak checks if the current value of counter matches the expected value, and if so, changes it to 1. If it fails, expected is updated to the current value of the counter, enabling a retry strategy.

Atomic Flag

An atomic_flag is a simpler, boolean type that can be used as a basic building block for locking algorithms that require just a boolean variable to represent the lock state:

cpp
1#include <atomic>
2
3std::atomic_flag lock = ATOMIC_FLAG_INIT;
4
5void criticalSection() {
6    while (lock.test_and_set(std::memory_order_acquire)) {
7        // busy-wait until lock is acquired
8    }
9    // critical section
10    lock.clear(std::memory_order_release);
11}

std::atomic_flag guarantees only test_and_set and clear operations, ensuring minimal overhead for lock-free algorithms.

std::atomic with Standard Data Types

std::atomic is not limited to integers. It can be specialized for other data types through:

  • std::atomic<bool>
  • std::atomic<char>
  • std::atomic<> std::shared_ptr
  • And more custom or complex types, provided the type supports bitwise operations.

Conclusion

The std::atomic framework is indispensable in the realm of modern C++ concurrency, providing a robust set of tools for atomic operations. While its utility is undeniable, leveraging std::atomic requires an understanding of memory models and atomic semantics to utilize its full potential effectively. By using std::atomic, developers can design systems that perform flawlessly in multi-threaded environments, paving the way for optimized and safe concurrent programming.

This look into std::atomic should give you a solid foundation to start utilizing atomic operations in your applications. Whether you're developing low-level system software or high-performance applications, std::atomic is a key abstraction that ensures data integrity and synchronization across threads.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.