boost::atomic can be used to create atomic variables. They are called atomic variables because all access is atomic. Boost.Atomic is used in multithreaded programs when access to a variable in one thread shouldn't be interrupted by another thread accessing the same variable. boost::atomic depends on the target platform supporting atomic variable access. Otherwise, boost::atomic uses locks.
1. boost::atomic
#include <boost/atomic.hpp> #include <thread> #include <iostream> boost::atomic<int> a(0); void thread() { ++a; } int main() { std::thread t1{thread}; std::thread t2{thread}; t1.join(); t2.join(); std::cout << a << std::endl;
std::cout.setf(std::ios::boolalpha);
std::cout << a.is_lock_free() << std::endl; //true return 0; }
boost::atomic works because some processors support atomic access on variables. If increaseing an int variable is an atomic operation, a lock isn't required.
You can call is_lock_free() on an atomic variable to check whether accessing the variable is done without a lock.
2. boost::memory_order_seq_cst
#include <boost/atomic.hpp> #include <thread> #include <iostream> boost::atomic<int> a{0}; void thread() { a.fetch_add(1, boost::memory_order_seq_cst); } int main() { std::thread t1{thread}; std::thread t2{thread}; t1.join(); t2.join(); std::cout << a << std::endl; return 0; }
The member function fetch_add() can take two parameters: the number by which a should be increased and the memory order. The memory order specifies the order in which access operations on memory must occur. Boost.Atomic supports specifying a memory order when accessing variables to make sure memory accesses occur in the desired order in a multithread program.
All memory accesses that appear before the fetch_add()
call must occur before this member function is executed. All memory accesses that appear after the fetch_add()
call must occur after this member function is executed. boost::memory_order_seq_cst is a strict boundary for memory accesses in both directions.
boost::memory_order_seq_cst is the most restrictive memory order.
3. boost::memory_order_relaxed
#include <boost/atomic.hpp> #include <thread> #include <iostream> boost::atomic<int> a{0}; void thread() { a.fetch_add(1, boost::memory_order_relaxed); } int main() { std::thread t1{thread}; std::thread t2{thread}; t1.join(); t2.join(); std::cout << a << std::endl; return 0; }
boost::memory_order_relaxed is the least restrictive memory order. It allows arbitrary reordering of memory accesses. This example works with this memory order because the threads access no variables except a.
4. boost::atomic with memory_order_release and memory_order_acquire
#include <boost/atomic.hpp> #include <thread> #include <iostream> boost::atomic<int> a{0}; int b = 0; void thread1() { b = 1; a.store(1, boost::memory_order_release); } void thread2() { while (a.load(boost::memory_order_acquire) != 1) ; std::cout << b << std::endl; } int main() { std::thread t1{thread1}; std::thread t2{thread2}; t1.join(); t2.join(); return 0; }
There arechoices between the most restrictive memory order, boost::memory_order_seq_cst, and the least restrictive one, boost::memory_order_relaxed. boost::memory_order_release and boost::memory_order_acquire.
Memory accesses that appear in the code before the boost::memory_order_release statement are executed before the boost::memory_order_release statement is executed. Compilers and processors must not move memory accesses from before to after boost::memory_order_release. However, they may move memory accesses from after to before boost::memory_order_release.
boost::memory_order_acquire works like boost::memory_order_release, but refers to memory accesses after boost::memory_order_acquire. Compilers and processors must not move memory accesses from after the boost::memory_order_acquire statement to before it. However, they may move memory accesses from before to after boost::memory_order_acquire.