So I'm starting to familiarize myself with C++11 <atomic>
types. In the past, when I had an atomic flag I would usually simply lock a mutex before accessing it. A common need would be to check if the flag is false
, and if so, atomically set it to true
and then do something. So basically this would be accomplished like this, where flag
is a simple bool
:
{
std::lock_guard<std::mutex> lock(my_mutex);
if (!flag)
{
flag = true;
// do something;
}
}
So, now I'm trying to figure out how the same thing can be accomplished with <atomic>
. The docs say that the assignment operator and operator T
of an atomic type are atomic operations. However, if I change flag
to std::atomic<bool>
, I imagine I can't simple say:
if (!flag)
{
flag = true;
// do something
}
... because even though the expression (!flag)
is atomic, and the assignment flag = true
is atomic, there's nothing to prevent another thread from modifying flag in between those two statements.
So, if I understand correctly here, the only proper usage - at all - of conditionals with atomic types, where the result of the conditional could modify the atomic variable, is to use the Compare and Swap operation? Am I correct?
So, I'd have to say:
bool expected = false;
if (flag.compare_exchange_weak(expected, true))
{
// do something
}
Am I correct in my understanding here?
Each instantiation and full specialization of the std::atomic template defines an atomic type. Objects of atomic types are the only C++ objects that are free from data races; that is, if one thread writes to an atomic object while another thread reads from it, the behavior is well-defined.
std::shared_ptr requires special attention in a multithreading environment. They are very special. They are the only non-atomic data types in C+ for which atomic operations exist.
It is not atomic. Atomic operations are not cheap and 99% of the time you do not need the atomicity. There are (IIRC) some other means to get atomic operations but std::swap() is not one of them.
std::atomic is not copyable or movable because its copy constructor is deleted and no move constructor is defined.
If you have multiple threads that are running the same code that need to do that flip, yes - you will need to use compare_exchange_weak()
or compare_exchange_strong()
for the precisely the reason you suggest (probably prefer strong).
However, it's not the case to say that this is the only proper usage of conditionals with atomics. If I have, say, one thread that only ever reads the atomic and one that writes to it, it's perfectly reasonable to use them the simple way... e.g.:
std::atomic<bool> done{false};
// thread 1
while (!done) {
....
}
// thread 2
stop() { done = true; }
There's no reason for me to do a done.compare_exchange_strong(expected, true)
there. That's overkill. It's really on a case-by-case basis.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With