Say I have a std::atomic called num:
std::atomic<double> num{3.1415};
as C++ does not completely support arithmetic operations for atomics except for "integral types", I cannot do:
num *= 10;
Instead, I'll have to do
num.store( num.load() * 10 );
Is it at risk of data race? e.g., after num.load() but before num.store(), is it possible that an another thread already updated the atomic?
While num.store(num.load() * 10)
still has a race condition, you can use the atomic_compare_exchange to do what you want.
The big-picture idea of using compare-and-exchange (sometimes known as compare-and-swap) operations is, in pseudo-code
do {
last-value-read = atomic-read-of-memory-location
desired-value = last-value-read * 10
} while (atomic-compare-and-exchange(atomic-update-to=desired_value,
only-if-memory-still=last-value-read));
(Some APIs, like C++s, make it easier by updating the last-value-read variable inside the compare-and-exchange operation if the memory content doesn't match the input value, so you just loop recalculating the desired-value.)
With this approach, you can implement your own more-complex atomic operations, even implement things like read-write locks where specific bits in the atomic value represent counters and boolean flags....