Is a comparison an atomic operation?
Is the following comparison an atomic action?开发者_StackOverflow社区 I.e., can it be reduced to a single CPU instruction?
char flag = 2;
for(;;)
{
if (!flag) // <-- this
break;
// sleep
}
Here's what I'm doing:
int main()
{
sf::Mutex Mutex;
char flag = 2;
coordinatorFunction(flag);
for(;;)
{
if (!flag)
break;
// sleep
}
}
void workerFunction(void* a)
{
char* p = static_cast<char*>(a);
// work
GlobalMutex.Lock();
--*p;
GlobalMutex.Unlock();
}
void coordinatorFunction(char& refFlag)
{
sf::Thread worker1(&workerFunction, &refFlag);
sf::Thread worker2(&workerFunction, &refFlag);
worker1.Launch();
worker2.Launch();
}
This is the wrong way to go about it.
Your main thread is burning up CPU cycles as fast as it can, doing nothing but waiting for flag
to reach zero. This test will fail every time it's attempted, except the last one. Instead of doing it in this manner, use the "join" facility that your thread objects most likely have to make the main thread suspend itself until all the workers complete.
This way, not coincidentally, you won't care if the test is atomic because you will not need it at all.
None of C++ operations is guaranteed to be atomic operation.
In C++ nothing is guaranteed to be atomic.
No. As far as I know, C++ makes no guarantees on what is atomic and what is not - that is platform specific. Even if your platform guarantees that a comparison can be made atomically, there's no guarantee that your C++ compiler will choose that instruction.
However, in practice, a comparison of a simple value type such as a char, int, float, etc is likely to be atomic. You still need to be aware of possible reordering of instructions however, both at the compiler level and the processor level. In this case, that may not matter, but in the general case, it can and does. You also need to be aware that a comparison-then-branch is not atomic even if the comparison is - so 2 threads can both enter the same block of code if you are trying to use a flag to regulate that access.
If you want proper guarantees, there are various Interlocked functions on Windows and the atomic builtins on gcc.
No, C++ makes no guarantees that any operation is atomic. The code in your question might well be compiled into a load from memory into a register, which might itself take multiple instructions, followed by a test.
The comparison is not atomic because it make take several machine language instructions to do it (load the from memory into a register etc.) Also due to memory model flexibility and caching the thread doing the test may not "see" the results of another thread right away.
You might be safe doing a simple test if the variable is marked volatile but this will be platform-specific. As others noted C++ itself makes no guarantee on this.
No.
A comparison involves reading both pieces of data as well as performing the actual comparison. The data can change in between the read and the compare instructions, so it's not atomic.
However, because you're comparing for equality, the _InterlockedCompareExchange
instrinsic (which is for the lock cmp xchg
instructions in x86) might do what you need, although it will involve replacing the data.
The question you should be asking is "is -- atomic"? Thats all that matters here. You want to do something when flag reaches 0.
You don't care about this scenario:
1. Main thread reads flag, and it is 1.
2. Worker changes flag with --
3. Main thread doesn't see that flag is actually 0.
Because in 1 ns, the main thread loops around and tries again.
You do care that -- is not atomic and two threads changing it at the same time will skip decrements:
1. Thread A reads flag, flag is 2
2. Thread B reads flag, flag is 2
3. Thread A decrements its copy of flag, 2, and writes to flag, flag is 1
4. Thread B decrements its copy of flag, also 2, and writes to flag, flag is 1.
You lost a decrement. you want to use __sync_fetch_and_sub(&flag, 1) which will atomically decrement flag.
Finally, spinning around a sleep is not the best way to do it. You want to either wait on a condition, wait on a signal. Have the worker threads raise the condition or the signal when they realize they have decremented flag to 0.
精彩评论