I am trying to answer this myself by going through various online resources (for example, this and this ), C ++ 11 Standard, as well as the answers given here.
Related questions are combined (for example, “ why! Expected? ” Is combined with “why put compare_exchange_weak () in a loop? ”) And answers are given accordingly.
Why should compare_exchange_weak () have to loop in almost all cases?
Typical Pattern A
You need to get an atomic update based on the value in the atomic variable. The error indicates that the variable is not updated with our desired value, and we want to repeat it. Please note that we don't care if this fails due to concurrent recording or false rejection. But we don’t care that we are making this change.
expected = current.load(); do desired = function(expected); while (!current.compare_exchange_weak(expected, desired));
A real example is multiple threads for adding an item to a singly linked list at the same time. Each thread first loads a pointer to the head, selects a new node and adds a head to this new node. Finally, he tries to replace the new node with his head.
Another example is the implementation of a mutex using std::atomic<bool> . No more than one thread can enter a critical section at a time, depending on which thread first set current to true and exits the loop.
Typical Pattern B
This is actually the pattern mentioned in Anthony's book. Unlike template A, you want the atomic variable to be updated once, but you don't care who does it. Until it updates, you try again. This is usually used with boolean variables. For example, you need to implement a trigger for a state machine to move. Which thread triggers the trigger.
expected = false; // !expected: if expected is set to true by another thread, it done! // Otherwise, it fails spuriously and we should try again. while (!current.compare_exchange_weak(expected, true) && !expected);
Please note that we generally cannot use this template to implement a mutex. Otherwise, several threads may be inside the critical section at the same time.
However, rarely use compare_exchange_weak() outside the loop. On the contrary, there are times when a strong version is used. For example.
bool criticalSection_tryEnter(lock) { bool flag = false; return lock.compare_exchange_strong(flag, true); }
compare_exchange_weak is not suitable here, because when it returns due to a false failure, it is likely that no one is still occupying a critical section.
The hungry stream?
Mention should be made of what happens if collateral failures continue to occur, thus starving the thread? Theoretically, this can happen on platforms when compare_exchange_XXX() is implemented as a sequence of instructions (e.g. LL / SC). Frequent access to the same cache line between LL and SC will result in continuous false failures. A more realistic example is due to silent planning, when all parallel threads alternate as follows.
Time | thread 1 (LL) | thread 2 (LL) | thread 1 (compare, SC), fails spuriously due to thread 2 LL | thread 1 (LL) | thread 2 (compare, SC), fails spuriously due to thread 1 LL | thread 2 (LL) v ..
May happen?
This will not happen forever, fortunately thanks to what C ++ 11 requires:
Implementations must ensure that weak exchange and exchange operations do not always return false, unless the atomic object has a value different from the expected one, or parallel modifications of the atomic object exist.
Why do we use compare_exchange_weak () and write the loop ourselves? We can just use compare_exchange_strong ().
It depends.
Case 1: when both should be used inside a loop. C ++ 11 says:
When comparisons and exchanges are in a loop, a weak version will give better performance on some platforms.
On x86 (at least for the time being. Maybe one day when using more cores, a similar LL / SC scheme will be used), the weak and strong version are essentially the same, since they both come down to the same cmpxchg instruction . On some other platforms where compare_exchange_XXX() not implemented atomically (there is no single hardware primitive), the weak version inside the loop can win the battle because the strong version will have to handle false failures and repeat accordingly.
But,
rarely, we may prefer compare_exchange_strong() over compare_exchange_weak() even in a loop. For example, when there are many things that need to be done between an atomic variable, a new value is loaded and calculated (see function() above). If the atom variable itself does not change often, we do not need to repeat the costly calculation for every false failure. Instead, we can hope that compare_exchange_strong() “absorb” such failures, and we simply repeat the calculation when it fails due to a change in the real value.
Case 2: when in a loop you need to use only compare_exchange_weak() . C ++ 11 also says:
If a loop is required for weak comparison and exchange and there is no strong loop, strong is preferable.
This usually happens when you run a loop to eliminate false crashes from the weak version. You repeat until the exchange is successful or unsuccessful due to simultaneous recording.
expected = false; // !expected: if it fails spuriously, we should try again. while (!current.compare_exchange_weak(expected, true) && !expected);
At best, he invents the wheels and performs the same actions as compare_exchange_strong() . Worse? This approach does not fully utilize the capabilities of machines that provide unrelated comparison and exchange of equipment.
Finally, if you are fixated on other things (for example, see “Typical Template A” above), then there is a good chance that compare_exchange_strong() should also be compare_exchange_strong() , which brings us back to the previous case.