Stdatomic Ref In C++ - C++ Programming Tutorial
C++ Course / Multithreading / Stdatomic Ref In C++

Stdatomic Ref In C++

BLUF: Mastering Stdatomic Ref In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Stdatomic Ref In C++

C++ is renowned for its efficiency. Learn how Stdatomic Ref In C++ enables low-level control and high-performance computing in the tutorial below.

The C++ feature std::atomicref serves as a powerful resource for secure, lock-free concurrent programming. It was integrated into the C++ Standard Library with the C++20 release. By providing a reference-like interface to atomic objects, this class removes the necessity for explicit locking mechanisms such as mutexes, enabling multiple threads to securely access and modify shared data. While std::atomic directly handles atomic objects, std::atomicref operates on existing non-atomic variables, granting them atomic access. This characteristic proves beneficial when dealing with older codebases or situations where converting the variable type to atomic is not feasible.

std::atomicref offers a crucial advantage by upholding atomicity assurances without compromising performance unnecessarily. To promote efficient concurrent access to shared data, it leverages hardware assistance for atomic operations whenever possible. By utilizing std::atomicref, developers can craft secure and efficient concurrent code that minimizes the risk of data conflicts and ensures reliable behavior in scenarios involving multiple threads.

When utilizing the std::atomicref class template, atomic operations are executed on the referenced object. This object is treated as atomic throughout its lifespan. To understand data races better, it is recommended to review the memory model. In scenarios where one thread writes to an atomic object and another reads from it, the behavior is precisely defined. Additionally, atomic object accesses facilitate inter-thread synchronization and organize non-atomic memory accesses based on the specified std::memoryorder directives.

All std::atomicrefs pointing to an object need to outlast the object. The object should only be interacted with through these std::atomicref instances, regardless of the presence of other std::atomicref instances pointing to the object. No std::atomicref objects' subcomponents should be accessed concurrently by any other std::atomic_ref object.

Defined in header __PRESERVE_6__

Example

template< class T >
struct atomic_ref;
template< class T >
struct atomic_ref<T*>;

Declaring std::atomic_ref:

If you intend to utilize the std::atomic_ref function, ensure to include the <atomic> header file.

Syntax:

It has the following syntax:

Example

std::atomic_ref<Type> ref(shared_var);

Parameters:

  • Type: It is the type of the shared variable.
  • shared_var: It is the shared variable to be accessed atomically.
  • Example 1:

Let's consider a scenario to demonstrate the std::atomic_ref function in C++.

Example

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

// Custom data structure
struct Coordinate {
    std::atomic<int> latitude;
    std::atomic<int> longitude;
};

int main() {
    Coordinate coord = {0, 0};

    auto updateCoordinates = [&]() {
        for (int i = 0; i < 5000; ++i) { // Changed number of iterations
            coord.latitude.fetch_add(1, std::memory_order_relaxed); // Increment latitude
            coord.longitude.fetch_add(1, std::memory_order_relaxed); // Increment longitude
        }
    };

    constexpr int totalThreads = 6; // Changed number of threads
    std::vector<std::thread> threadPool;
    threadPool.reserve(totalThreads);

    for (int i = 0; i < totalThreads; ++i) {
        threadPool.emplace_back(updateCoordinates);
    }

    for (auto& thread : threadPool) {
        thread.join();
    }

    std::cout << "Final coordinates: (" << coord.latitude.load() << ", " << coord.longitude.load() << ")" << std::endl;

    return 0;
}

Output:

Output

Final coordinates: (30000, 30000)

Explanation:

  • In this example, we define a basic Point structure with two integer members, x and y.
  • For atomic access, we create a std::atomic_ref named atomicPoint and an instance of Point named point.
  • Within the lambda function modifyPoint, every thread iteratively increases the atomicPoint's x and y coordinates.
  • Multiple threads are created to change the Point structure simultaneously.
  • Each thread increases x and y by 10,000 times, so once all threads have completed running, we print the point structure's final coordinates, which should be (30000, 30000).
  • Example 2:

Let's consider another instance to demonstrate the std::atomic_ref function in C++.

Example

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

int main() {
    int counter = 0;
    std::atomic_ref<int> atomicCounter(counter);

    auto incrementCounter = [&]() {
        for (int i = 0; i < 10000; ++i) {
            atomicCounter.fetch_add(1, std::memory_order_relaxed);
        }
    };

    constexpr int numThreads = 4;
    std::vector<std::thread> threads;
    threads.reserve(numThreads);

    // Create threads to increment the counter
    for (int i = 0; i < numThreads; ++i) {
        threads.emplace_back(incrementCounter);
    }

    // Wait for all threads to finish
    for (auto& thread : threads) {
        thread.join();
    }

    // Output the final value of the counter
    std::cout << "Final counter value: " << atomicCounter << std::endl;

    return 0;
}

Output:

Output

Final counter value: 40000

Explanation:

  • In this example, our counter variable's initial value is 0.
  • We construct an atomicCounter std::atomic_ref to enable atomic access to the counter.
  • Every thread repeatedly increases the atomicCounter by 1 inside the lambda function incrementCounter.
  • Concurrently incrementing the counter requires the creation of multiple threads.
  • Once every thread has completed its execution, we use the atomicCounter object to output the counter's final value.
  • Conclusion:

In the realm of C++, std::atomicref presents a robust tool for enabling secure, lock-free concurrent programming. This is achieved by furnishing atomic objects with a reference-style interface, eliminating the necessity for explicit locking mechanisms such as mutexes. Consequently, multiple threads can securely interact with and modify shared data. This functionality is particularly advantageous in situations where altering the variable type directly to atomic proves impractical, such as when dealing with legacy code. Leveraging hardware support for atomic operations, std::atomicref ensures reliable concurrent access to shared data. By incorporating this tool, developers can craft concurrent code that is both secure and efficient, mitigating the risks of data races and ensuring consistent performance in multi-threaded environments.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience