Это неопределенное поведение для одного потока, чтобы записать переменную volatile
, в то время как другой поток читает ее, в соответствии с C11.volatile
доступы также не упорядочены по отношению к другим доступам.Вы хотите, чтобы atomic_store_explicit(&head, new_value, memory_order_release)
в записывающем устройстве и atomic_load_explicit(&head, memory_order_acquire)
в считывающем устройстве создавали синхронизацию acq / rel и заставляли компилятор сделать хранилища в вашей структуре видимыми до сохранения до head
, что указывает читателю на наличие новых данных..
(tail
является приватным для потока читателя, поэтому у писателя нет механизма, чтобы ждать, пока читатель увидит новые данные, прежде чем записывать больше. Так что технически возможна гонка за содержимым структуры, еслипоток записи пишет снова, в то время как читатель все еще читает. Таким образом, структура также должна быть _Atomic
).
Возможно, вы захотите установить seq-lock, когда средство записи обновляет порядковый номер ичитатель проверяет его до и после копирования переменных. https://en.wikipedia.org/wiki/Seqlock Это позволяет обнаруживать и повторять попытки в тех редких случаях, когда писатель находился в середине обновления, когда читательскопировал данные.
Это очень хорошо для ситуаций только для записи / только для чтения, особенно если вам не нужно беспокоиться очитатель пропустил обновление.
Посмотрите мою попытку SeqLock в C ++ 11: Реализация 64-битного атомного счетчика с 32-битной атомикой , а также , как реализовать блокировку секвлокас использованием атомарной библиотеки c ++ 11
и переупорядочение GCC при нагрузке с помощью `memory_order_seq_cst`.Разрешено ли это? показывает другой пример (этот вызывает ошибку gcc).
Портирование их из C ++ 11 std :: atomic в C11 stdatomic должно быть простым.Убедитесь, что вы используете atomic_store_explicit
, потому что порядок памяти по умолчанию для обычного atomic_store
равен memory_order_seq_cst
, что медленнее.
Мало что вы можете сделать, на самом деле ускорит процесс записиего магазины видны во всем мире .Ядро процессора уже фиксирует хранилища из буфера хранилища в свой L1d как можно быстрее (соблюдая ограничения модели памяти x86, которая не допускает переупорядочения StoreStore).
Об Xeon см. Когда процессор сбрасывает значение в буфере хранения в кэш-память L1? для получения некоторой информации о различных режимах Snoop и их влиянии на задержку между ядрами в одном сокете.
Кэши на нескольких ядрахявляются когерентными, используя MESI для поддержания когерентности.
Пожалуй, лучше всего сделать ожидание вращения читателя для атомарной переменной, используя _mm_pause()
внутри цикла вращения, чтобы избежать конвейера ошибочных предположений порядка памятисбрасывается при выходе из спин-цикла.
Вы также не хотите просыпаться в середине записи и должны повторить попытку.Возможно, вы захотите поместить счетчик seq-lock в ту же строку кэша, что и данные, поэтому, надеюсь, эти хранилища могут быть объединены в буфере хранилища пишущего ядра.