Ответ 1
(CAS = Compare And Swap = C++ compare_exchange_weak
функция, которая в x86 обычно компилируется в инструкцию x86 lock cmpxchg
, которая может выполняться только в том случае, если ей принадлежит строка кэша в Exclusive или Modified. MESI государство).
lock_shared
выглядит хорошо: вращение только для чтения с попыткой CAS только тогда, когда это кажется возможным, лучше для производительности, чем вращение на CAS или атомное приращение. Вы уже должны были выполнить проверку только для чтения, чтобы избежать изменения -1
на 0
и разблокировки блокировки записи.
На платформе x86 поместите _mm_pause()
в путь повторения цикла вращения, чтобы избежать ядерных ошибок конвейера неправильной спекуляции порядка памяти при выходе из цикла вращения только для чтения, и во время вращения похищайте меньше ресурсов из другой гиперпотоки. (Используйте цикл while()
, а не do{}while()
, поэтому пауза запускается только после однократного сбоя. pause
на Skylake и позже ждет около 100 циклов, поэтому избегайте этого в быстром пути.)
Я думаю, что unlock_shared
должен использовать mo_release
, а не mo_relaxed
, так как ему необходимо упорядочить загрузки из общей структуры данных, чтобы убедиться, что записывающее устройство не начинает запись до того, как произойдет загрузка из критического раздела читателя., (Переупорядочение в LoadStore характерно для слабо упорядоченных архитектур, хотя x86 выполняет переупорядочение только в StoreLoad.) Операция Release упорядочивает предшествующие загрузки и удерживает их в критической секции.
(в записи
lock
)://можно ли использовать memory_order_relaxed, если только один поток блокирует запись?
Нет, вам все еще нужно хранить записи в критической секции, поэтому CAS все еще нужно синхронизировать с (в терминологии C++) хранилищами релизов из unlock_shared
.
https://preshing.com/20120913/acquire-and-release-semantics/ имеет хорошее изображение, которое показывает односторонний барьерный эффект релиз-магазина или приобретения-загрузки.