Я думаю, вам нужно сделать шаг назад и сначала определить все ваши варианты использования. Достоинства __sync по сравнению с C11 atomics в стороне, лучше сначала определить ваши потребности (т.е. __sync/atomics - это решения, которые не нужны).
Ядро Linux является одним из самых тяжелых и изощренных пользователей блокировок, атомарности и т. д., и атомарность C11 недостаточно мощна для этого. См. https://lwn.net/Articles/586838/.
Например, вам может быть гораздо лучше обернуть вещи парами pthread_mutex_lock
/pthread_mutex_unlock
. Объявление структуры C11 атомарной не гарантирует атомарный доступ ко всей структуре, а только к ее частям. Итак, если вам нужно, чтобы следующее было атомарным:
glob.x = 5;
glob.y = 7;
glob.z = 9;
Вам лучше обернуть это в пару pthread_mutex_*. Для сравнения, внутри ядра Linux это будут спин-блокировки или RCU. Фактически, вы также можете использовать RCU. Обратите внимание, что выполнение:
CAS(glob.x,5)
CAS(glob.y,7)
CAS(glob.z,9)
не совпадает с парой мьютексов, если вы хотите обновить все или ничего.
Я бы обернул вашу реализацию тонким слоем. Например, лучшим способом может быть __sync на одной арке [скажем, BSD] и атомарность на другой. Абстрагируя это в файл .h с макросами/встроенными строками, вы можете везде писать «общий код» без большого количества #ifdef's
.
Я написал структуру/объект кольцевой очереди. Его программа обновления может использовать CAS [для этого я написал свой собственный встроенный ассемблер], pthread_mutex_*, блокировку вращения ядра и т. д. Фактический выбор которых контролировался одним или двумя #ifdef's
внутри my_ring_queue.h
Еще одно преимущество абстракции: вы можете передумать в будущем. Предположим, вы сделали ранний выбор __sync или atomics. Вы кодируете это в 200 местах в 30 файлах. Затем наступает «большой упс», когда вы понимаете, что это был неправильный выбор. Происходит много редактирования. Поэтому никогда не помещайте голый [скажем] __sync_val_compare_and_swap
ни в один из ваших файлов .c. Поместите его один раз в my_atomics.h как что-то вроде #define MY_CAS_VAL(...) __sync_val_compare_and_swap(...)
и используйте MY_CAS_VAL
Вы также можете уменьшить количество мест, требующих блокировки между потоками, используя локальное хранилище потока для определенных вещей, таких как выделение/освобождение подпула.
Вы также можете использовать сочетание CAS и блокировки. Некоторые конкретные виды использования лучше использовать с низкоуровневым CAS, а другие будут более эффективными с парами мьютексов. Опять же, полезно, если вы можете сначала определить свои потребности.
Кроме того, рассмотрите окончательный сценарий катастрофы: компилятор не поддерживает атомарность, а __sync недоступен [или не работает] для архитектуры, в которую вы компилируете. Что тогда?
В этом случае обратите внимание, что все операции __sync могут быть реализованы с использованием пар pthread_mutex. Это ваш аварийный запасной вариант.
person
Craig Estey
schedule
24.10.2015
__atomic
, которые ближе к тому, что предоставляет C11, а начиная с gcc 4.9 или около того у вас действительно есть C11 atomics. - person Jens Gustedt   schedule 24.10.2015