C++ | Atomic Types
Multi-threaded Access
Section titled “Multi-threaded Access”An atomic type can be used to safely read and write to a memory location shared between two threads.
A Bad example that is likely to cause a data race:
#include <thread>#include <iostream>
//function will add all values including and between 'a' and 'b' to 'result'void add(int a, int b, int * result) { for (int i = a; i <= b; i++) { *result += i; }}
int main() { //a primitive data type has no thread safety int shared = 0;
//create a thread that may run parallel to the 'main' thread //the thread will run the function 'add' defined above with paramters a = 1, b = 100, result = &shared //analogous to 'add(1,100, &shared);' std::thread addingThread(add, 1, 100, &shared);
//attempt to print the value of 'shared' to console //main will keep repeating this until the addingThread becomes joinable while (!addingThread.joinable()) { //this may cause undefined behavior or print a corrupted value //if the addingThread tries to write to 'shared' while the main thread is reading it std::cout << shared << std::endl; }
//rejoin the thread at the end of execution for cleaning purposes addingThread.join();
return 0;}The above example may cause a corrupted read and can lead to undefined behavior.
An example with thread safety:
#include <atomic>#include <thread>#include <iostream>
//function will add all values including and between 'a' and 'b' to 'result'void add(int a, int b, std::atomic<int> * result) { for (int i = a; i <= b; i++) { //atomically add 'i' to result result->fetch_add(i); }}
int main() { //atomic template used to store non-atomic objects std::atomic<int> shared = 0;
//create a thread that may run parallel to the 'main' thread //the thread will run the function 'add' defined above with paramters a = 1, b = 100, result = &shared //analogous to 'add(1,100, &shared);' std::thread addingThread(add, 1, 10000, &shared);
//print the value of 'shared' to console //main will keep repeating this until the addingThread becomes joinable while (!addingThread.joinable()) { //safe way to read the value of shared atomically for thread safe read std::cout << shared.load() << std::endl; }
//rejoin the thread at the end of execution for cleaning purposes addingThread.join();
return 0;}The above example is safe because all store() and load() operations of the atomic data type protect the encapsulated int from simultaneous access.
Syntax
Section titled “Syntax”- std::atomic
- std::atomic_flag
Remarks
Section titled “Remarks”std::atomic allows atomic access to a TriviallyCopyable type, it is implementation-dependent if this is done via atomic operations or by using locks. The only guaranteed lock-free atomic type is std::atomic_flag.