Термин «без блокировки» имеет два значения:
значение для компьютерной науки: застревание одного потока не может препятствовать другим. Эту задачу невозможно сделать без блокировки, вам нужно потоков, чтобы ждать друг друга .(https://en.wikipedia.org/wiki/Non-blocking_algorithm)
с использованием атомной блокировки без блокировки. Вы в основном создаете свой собственный механизм для создания потокового блока, ожидая в неприятном цикле вращения без отступления, чтобы в конечном итоге освободить процессор.
Каждая отдельная стандартная операция загрузки и хранения отдельно не блокируется, но вы используете их для создания своего рода двухпотоковой блокировки.
Вашпопытка выглядит корректной для меня. Я не вижу, как поток может «пропустить» обновление, потому что другой поток не напишет другой, пока не завершится этот. И я не вижу пути для обоих потоковбыть в их критических секциях сразу.
Более интересный тест будет использовать разблокированные операции stdio, такие как
fputs_unlocked("ping\n", stdio);
, чтобы использовать (и зависеть) тот факт, чтовы уже гарантировали взаимное исключение между потоками. См. unlocked_stdio (3) .
И тестирование с выводом, перенаправленным в файл, поэтому stdio полностью буферизуется, а не буферизируется строкой. (Aсистемавызов как write()
в любом случае полностью сериализуется, например atomic_thread_fence(mo_seq_cst)
.)
Он либо без блокировки, либо не компилируется
Хорошо, почемутот странный?Вы решили сделать это.Это необязательно;алгоритм по-прежнему будет работать на реализациях C без постоянной блокировки atomic_int
.
atomic_bool
может быть лучшим выбором, будучи свободным от блокировки на большем количестве платформ, включая 8-битныеплатформы, где int
принимает 2 регистра (потому что он должен быть не менее 16-битным).Реализации могут свободно делать atomic_bool
4-байтовым типом на платформах, где это более эффективно, но IDK, если таковые вообще есть.(На некоторых платформах, отличных от x86, загрузка / хранение байтов обходится дополнительным циклом задержки чтения / записи в кэш. Здесь это пренебрежимо мало, потому что вы всегда имеете дело с пропуском кеша между ядрами.)
ВыЯ думаю, atomic_flag
будет правильным выбором для этого, но он обеспечивает только тестирование и настройку и очистку, как операции RMW. Не обычная загрузка или сохранение.
Такие предположения работают только в том случае, если мы знаем аппаратную модель памяти, которая не является переносной
Да, но этоas-code gen без препятствий генерируется только при компиляции для x86 .Компиляторы могут и должны применять правило as-if для создания asm, который выполняется на цели компиляции , как если бы источник C работал на абстрактной машине C.
Использование stdatomics с pthreads
Гарантирует ли стандарт ISO C, что поведение атома должно быть четко определено со всеми реализациями потоков (такими как pthreads, более ранние LinuxThreads и т. Д ...)
НетISO C ничего не говорит о таких расширениях языка, как POSIX.
В сноске (не нормативной) говорится, что атомы без блокировки должны быть безадресными, чтобы они работали между различными процессами, получающими доступта же общая память.(Или, может быть, эта сноска есть только в ISO C ++, я не пошел и не перепроверил).
Это единственный случай, когда я могу подумать, что ISO C или C ++ пытается предписывать поведение для расширений.
Но стандарт POSIX , мы надеемся, что-то говорит о stdatomic!Вот где вы должны смотреть;он расширяет ISO C, а не наоборот, поэтому стандарт pthreads должен указывать, что его потоки работают как C11 thread.h
, а атомика работает.
На практике, конечно, stdatomicна 100% подходит для любой реализации потоков, где все потоки используют одно и то же виртуальное адресное пространство. Это включает в себя такие вещи без блокировки, как _Atomic my_large_struct foo;
.