Есть ли опыт работы с атомарными типами в C++
Что проверяют
За вопросом «есть ли опыт» стоит проверка, писали ли вы многопоточный код руками и понимаете ли, что атомарность и порядок памяти это разные вещи. Следом почти всегда спрашивают про memory_order и про разницу compare_exchange_weak и compare_exchange_strong.
Как отвечать
Отвечайте от своего опыта: где именно применяли атомики и почему выбрали их, а не мьютекс. Примеры, которые интервьюер узнает: флаг остановки воркера std::atomic<bool>, счётчики метрик, счётчик ссылок, очередь с одним писателем и одним читателем на кольцевом буфере. Детали и цифры нужны свои.
Технический каркас, который стоит показать по ходу:
- операции:
load,store,exchange,fetch_add,compare_exchange_weakиcompare_exchange_strong. Слабый вариант может ложно вернуть неудачу, поэтому его крутят в цикле; - порядок памяти: по умолчанию
memory_order_seq_cst. Для публикации данных между потоками хватает парыreleaseпри записи иacquireпри чтении, для простого счётчикаrelaxed; - lock-free: не каждый
std::atomic<T>работает без блокировок, это проверяютis_lock_free()илиis_always_lock_free(C++17); - C++20:
waitиnotify_oneу атомиков,std::atomic_refдля обычной переменной,std::atomic<std::shared_ptr<T>>.
Если реального опыта нет, так и скажите и покажите понимание темы: разберите, как написали бы спинлок на std::atomic_flag и как проверили бы код через ThreadSanitizer.
Где спотыкаются
Считают, что атомарные операции дают атомарную логику. if (x.load() == 0) x.store(1); это две операции, между ними влезет другой поток, здесь нужен compare_exchange.
Используют volatile для синхронизации потоков. В C++ оно не даёт ни атомарности, ни упорядочивания.
Не знают о false sharing: два горячих атомика в одной кэш-линии тормозят друг друга. Помогает выравнивание через alignas(std::hardware_destructive_interference_size).