Comments 12
Все-таки, в первом листинге нужно объявить a как volatile
что это даст?
В таком случае компилятор не будет иметь права оптимизировать доступы к этой переменной, насколько мне известно. Если попробовать скомпилировать с флагом -O3, то вы (не гарантированно, конечно) получите просто ноль из-за оптимизаций.
Атомики дают такие же гарантии, даже больше.
Это старый подход, когда volatile использовался для предотвращения оптимизаций компилятором. Но это было валидно, до того как memory model была формализована для С и С++.
Наверное выглядело бы как нагромождение, т.к. код немного вырван из контекста и не понятно зачем там volatile. Конечно, если человек интересуется тем, как работают атомики, наверное должен знать, как работает volatile, но тут, буд-то несёт больше лишнюю нагрузку нежели нужный для осмысления примера
Спасибо за статью!
Я бы рекомендовал использовать
weakдля спинлоков (CASв цикле) иstrongв остальных случаях, но, строго говоря, никто не мешает вам делать иначе.
На моей памяти зависит от архитектуры, для x86-64 weak/strong cas операции работают одинаково, в то же время для arm ризличны (больше к слову).
Про модель памяти (memory_ordering и отношения: happens before и прочие) как-то бегло по сравнению с разбором на уровне памяти/кешей, хотя там рассказывать много можно)
Спасибо за хороший материал, дополню отличным видео по теме от Fedor Pikus
До середины где-то прочитал и всё время какое-то раздражение от несовпадения того что в коде написано и что потом говорится.
В одном потоке инкремент, в другом декремент, а в описании дважды инкремент)
В разделе с описанием compare_exchange_strong тоже не попадание в результате ожидаемого вывода результата.
Непонятное ощущение. Закрыл, чтобы не взрывало мозг. Хотя как "разрывать нужно нейронные связи" - сработала отлично
В целом информативный текст. Поправить и отличный(до половины ибо дальше не читал)
Было бы неплохо добавить иллюстрации ассемблерного кода для memory orderring , какие инструкции сообщают процессору не менять последовательность, на разных архитектурах.
Чего же вы бросили в конце, не дописали?
std::atomic<T> в стандарте разрешён только для trivially copyable типов.
«Сложный» класс (c виртуалами, нестандартным деструктором, std::string внутри и т.п.) туда напрямую класть нельзя.
Для сложного состояния используется паттерн: атомарный указатель/shared_ptr на объект, а не сам объект в atomic.
Да, атомарный shared_ptr - это, наверное, теретически лучший паттерн для реализации атомарного сложного состояния, но в современных компиляторах (я проврял clang 21.1.0 gcc 15.2 ) он не lock free, к сожалению (по крайней мере для x86-64). Поэтому смысла в нем пока немного. Правда, если количество взаимодействющих нитей ограничено, возможны его сравнительно несложные самодельные lock-free паллиативы (с заранее заготовленным небольшим пулом объектов).
Введение в атомики. C++