bool compare_exchange_weak (T& expected, T val, ..);
compare_exchange_weak()
является одним из примитивов обмена обменом, представленным в С++ 11. Он слабый в том смысле, что он возвращает false, даже если значение объекта равно expected
. Это связано с ложным сбоем на некоторых платформах, где для его реализации используется последовательность инструкций (вместо одной как на x86). На таких платформах коммутатор контекста, перезагрузка одного и того же адреса (или строки кэша) другим потоком и т.д. Может привести к сбою примитива. Это spurious
, поскольку это не значение объекта (не равное expected
), которое не выполняет операцию. Вместо этого, это вопрос времени.
Но что меня озадачивает то, что сказано в С++ 11 Standard (ISO/IEC 14882),
29.6.5.. Следствием ложного отказа является то, что почти все виды использования слабых compare-and-exchange будет в цикле.
Почему он должен находиться в цикле в почти всех использованиях? Означает ли это, что мы будем зацикливаться, когда это произойдет из-за ложных сбоев? Если это так, почему мы пытаемся использовать compare_exchange_weak()
и сами писать цикл? Мы можем просто использовать compare_exchange_strong()
, который, я думаю, должен избавиться от ложных сбоев для нас. Каковы распространенные случаи использования compare_exchange_weak()
?
Другой вопрос. В своей книге "С++ Concurrency In Action" Энтони говорит,
//Because compare_exchange_weak() can fail spuriously, it must typically
//be used in a loop:
bool expected=false;
extern atomic<bool> b; // set somewhere else
while(!b.compare_exchange_weak(expected,true) && !expected);
//In this case, you keep looping as long as expected is still false,
//indicating that the compare_exchange_weak() call failed spuriously.
Почему !expected
присутствует в условии цикла? Есть ли там, чтобы предотвратить, что все потоки могут голодать и не прогрессировать в течение некоторого времени?
Изменить: (последний вопрос)
На платформах, где нет единой аппаратной инструкции CAS, как слабая, так и сильная версия реализованы с использованием LL/SC (например, ARM, PowerPC и т.д.). Так есть ли разница между двумя следующими циклами? Почему, если таковые имеются? (Для меня они должны иметь аналогичную производительность.)
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_weak(..))
{ .. }
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_strong(..))
{ .. }
Я подхожу к этому последнему вопросу, вы, ребята, все упоминаете, что в цикле может быть разница в производительности. Он также упоминается в стандарте С++ 11 (ISO/IEC 14882):
Когда сравнение и обмен находятся в цикле, слабая версия даст более высокая производительность на некоторых платформах.
Но, как было проанализировано выше, две версии в цикле должны давать такую же/подобную производительность. Что я пропущу?