Stdmemory Order Enum In C++

In this article, we will discuss the std::memory_order enum in C++ with its example.

  • The order in which memory accesses, including conventional, and non-atomic memory accesses, are to be placed around an atomic operation is specified by the std::memory_order function.
  • When numerous threads simultaneously read and writeto multiple variables in a multi-core system without any limitations, one thread may notice that the values change in a different order than the order in which another thread wrote them.
  • Different reader threads may even appear to have different modifications in the same order.
  • Because of compiler changes permitted by the memory model, certain identical effects may even manifest on systems with a single CPU.
  • Atomic Functions:

  • Operations that are assured to be carried out without interference from other threads are known as atomic operations .
  • It supports for atomic operations in C++ is given by the <atomic> header, which includes types like std::atomic and functions like std::atomicload and std::atomicstore.
  • All atomic actions in the library have default behavior that allows for sequentially consistent ordering (see discussion below).
  • The library's atomic actions can be given an extra std::memory_order parameter to indicate the precise constraints, beyond atomicity, that the compiler and processor must apply for that operation.
  • Memory Order Enum in std:

The order of std::memory, the memory ordering requirements for atomic operations are specified using an enum. It offers multiple settings, each denoting a distinct ordering and synchronization assurance level. The primary choices are:

memoryorderrelaxed: It is the least restrictive ordering requirement. As long as the program's behavior complies with the sequential consistency model, it permits the compiler and processor to rearrange memory operations. As long as the outcome is the same as if the operations were carried out in the program's original order, it is acceptable to reorder the actions.

memoryorderconsume: In comparison to memoryorderacquire, this type of synchronization is less effective. It is designed to be used in activities that need consumption, like reading a value from memory that depends on another value read from memory. It makes sure the dependent operation doesn't happen before the consuming operation.

memoryorderacquire: It ensures that no previous activities that depend on the value read from memory are rearranged about the read-modify-write (RMW) operation carried out by the atomic operation. It ensures that we will obtain the most recent value written by another thread.

memoryorderrelease: It makes sure that no additional operations that depend on the value recorded are done after the write operation carried out by the atomic operation. It ensures that other threads will be able to see the written value.

memoryorderacqrel: Combining memoryorderrelease and memoryorderacquire results in memoryorderacqrel: It ensures that the most recent value written by another thread is retrieved and that the written value is visible to other threads.

memoryorderseq_cst: The best ordering guarantee is given by this. It guarantees the sequential consistency of every atomic action about every other operation. It indicates that the sequence in which the operations are carried out is the same as the order in which they appear in the program.

Pseudocode:

Example

// Shared data
atomic int data
atomic bool ready
// Producer function
function producer():
    // Produce some data
    int value = 42
    // Store the data with memory_order_release
    data.store(value, memory_order_release) 
    // Signal readiness to the consumer
    ready.store(true, memory_order_release)
// Consumer function
function consumer():
    // Wait for the producer to signal readiness
    while not ready.load(memory_order_acquire):
        // Do nothing or yield
    // Load the data with memory_order_acquire
    int value = data.load(memory_order_acquire)    
    // Consume the data
    print "The answer is " + value
// Main function
function main():
    // Start the producer and consumer threads
    start_thread(producer)
    start_thread(consumer)   
    // Wait for the threads to finish
    join_thread(producer)
    join_thread(consumer)

Example:

Let us take an example to illustrate the std::memory_order enum in C++.

Example

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

std::atomic<int> data;
std::atomic<bool> ready;

void producer() {
    // Produce some data
    int value = 42;
    
    // Store the data with memory_order_release
    data.store(value, std::memory_order_release);
    
    // Signal readiness to the consumer
-ready.store(true, std::memory_order_release);
}

void consumer() {
    // Wait for the producer to signal readiness
    while (!ready.load(std::memory_order_acquire)) {
        // Do nothing or yield
        std::this_thread::yield();
    }
    
    // Load the data with memory_order_acquire
    int value = data.load(std::memory_order_acquire);
    
    // Consume the data
    std::cout << "The answer is " << value << std::endl;
}

int main() {
    // Start the producer and consumer threads
    std::thread producer_thread(producer);
    std::thread consumer_thread(consumer);
    
    // Wait for the threads to finish
    producer_thread.join();
    consumer_thread.join();
    
    return 0;
}

Output:

Conclusion:

In conclusion, you can declare the memory ordering requirements for atomic operations using the C++ std::memory_order enum. It is essential to comprehend this memory ordering to write accurate and effective concurrent applications. The program's unique requirements and the required degree of synchronization determine which memory order should be used.

Input Required

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