Понимание std:: atomic:: compare exchange weak() в C++11
bool compare_exchange_weak (T& expected, T val, ..);
compare_exchange_weak() является одним из примитивов compare-exchange, представленных в C++11. Это ... слабый в том смысле, что он возвращает false, даже если значение объекта равно expected. Это связано с тем, что ложная неудача на некоторых платформах, где для его реализации используется последовательность инструкций (а не одна, как на x86). На таких платформах переключение контекста, перезагрузка того же адреса (или строки кэша) другим потоком и т. д. может привести к сбою примитива. Это spurious поскольку это не значение объекта (не равное expected), которое завершает операцию. Вместо этого, это своего рода временные проблемы.
, но то, что озадачивает меня, так это то, что сказано в стандарте C++11 (стандарт ISO/МЭК 14882),
29.6.5
..
Следствием ложного отказа является то, что почти все виды использования слабых
сравнение и обмен будут в цикле.
Почему он должен быть в цикле в почти все виды применения ? Означает ли это, что мы будем петлять, когда это не удается из-за ложные неудачи? Если это так, то почему мы используем compare_exchange_weak() и пишем цикл сами? Мы можем просто использовать compare_exchange_strong(), которые, я думаю, должны избавить нас от ложных неудач. Каковы общие случаи использования compare_exchange_weak()?
Еще один вопрос, связанный с этим. В своей книге "C++ 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 находится в состоянии цикла? Существует ли это для предотвращения того, что все нити могут голодать и не прогрессировать в течение некоторого времени?
Edit: (последний раз вопрос)
На платформах, где не существует единой аппаратной инструкции 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(..))
{ .. }
Я подхожу к этому последнему вопросу, который вы, ребята, все упоминаете, что, возможно, есть разница в производительности внутри цикла. Это также упоминается стандартом C++11 (ISO / IEC 14882):
Когда сравнение и обмен находятся в цикле, слабая версия будет давать
лучшая производительность на некоторых платформах.
Но, как было проанализировано выше, две версии в цикле должны давать одинаковую / подобную производительность. Чего же мне не хватает?
Comments