В программе, над которой я работаю, у меня много следующего кода:
pthread_mutex_lock( &frame->mutex );
frame->variable = variable;
pthread_mutex_unlock( &frame->mutex );
Очевидно, что это пустая трата циклов ЦП, если среднюю инструкцию можно просто заменить атомарным хранилищем. Я знаю, что gcc вполне на это способен, но мне не удалось найти много документации по таким простым потокобезопасным атомарным операциям. Как мне заменить этот набор кода атомарной операцией?
(Я знаю, что простые хранилища теоретически должны быть атомарными, но я не хочу надеяться, что оптимизатор не испортит их атомарность в какой-то момент процесса.)
Уточнение: мне не нужно, чтобы они были строго атомарными; эти переменные используются исключительно для синхронизации потоков. То есть поток B считывает значение, проверяет, правильно ли оно, и, если оно неверно, засыпает. Таким образом, даже если поток A обновляет значение, а поток B не осознает его обновление, это не проблема, поскольку это просто означает, что поток B спит, когда это действительно не нужно, а когда он просыпается, значение будет быть правильным.
Кстати, __atomic_store или __atomic_store_n кажется более подходящим.





Вы можете проверить документацию gcc. Для текущей версии gcc (4.3.2) это будет глава 5.47 Встроенные функции для доступа к атомарной памяти - для других версий gcc, пожалуйста, проверьте свою документацию. Это должно быть в главе 5 - Расширения семейства языков C.
Между прочим, компилятор C не дает никаких гарантий относительно того, что простые операции хранения являются атомарными. Вы не можете полагаться на это предположение. Чтобы машинный код операции выполнялся атомарно, ему нужен префикс LOCK.
Более того, эти встроенные функции для доступа к атомарной памяти также поддерживаются ICC (в случае, если вы собираетесь переносить компилятор). Я думаю, что для GCC атомарные функции поддерживаются начиная с версии 4.1, поэтому обязательно используйте #ifdef для обеспечения версии gcc или icc!
Значит, в случае одновременного доступа __sync_add_and_fetch ненадежен? и мы должны использовать lock addx напрямую с встраиванием сборки?
AFAIK, вы не можете префикс инструкций MOV с помощью LOCK; это разрешено только для операций RMW. Но если он делает использует простое хранилище, ему также может понадобиться барьер памяти, который неявно связан с мьютексом, а также с инструкциями, разрешающими LOCK.
На x86 и большинстве других архитектур выровненные 4-байтовые операции чтения и записи всегда атомарны. Однако оптимизатор может пропускать / переупорядочивать операции чтения и записи в одном потоке.
Что вы хотите сделать, так это сообщить компилятору, что другие потоки могли коснуться этой области памяти. (Побочный эффект pthread_mutex_lock сообщает компилятору, что другие потоки могли затронуть любую часть памяти.) Вы можете увидеть, что volatile рекомендуется, но этого нет в спецификации C, и GCC не интерпретирует volatile таким образом.
asm("" : "=m" (variable));
frame->variable = variable;
- это специфичный для GCC механизм, говорящий, что «variable был записан, перезагрузите его».
Помимо этого, кэш процессора может скрывать или переупорядочивать операции чтения и записи по отношению к другим процессорам ... поэтому требуется ограждение памяти. pthread_mutex_lock предоставляет это, но это очень зависит от архитектуры.
До определенного момента атомарные операции в C предоставлялись прямо из исходников ядра через заголовок atomic.h.
Однако использование заголовков ядра непосредственно в коде пользовательского пространства - очень плохая практика, поэтому файл заголовка atomic.h был удален некоторое время назад. Вместо этого мы можем теперь использовать «атомарные встроенные модули GCC», которые являются гораздо лучшим и более надежным подходом.
Есть очень хорошее объяснение, предоставленное Тудором Голубенко в его блоге. Он даже обеспечивает замену исходного файла atomic.h на случай, если у вас есть какой-то код, который в этом нуждается.
К сожалению, я новичок в stackoverflow, поэтому могу использовать только одну ссылку в своих комментариях, поэтому проверьте сообщение Тюдора и получите просветление.
Как я вижу, вы используете платформу GNU для разработки, поэтому можно с уверенностью сказать, что glic предоставляет тип данных int с атомарными возможностями, 'sig_atomic_t'. Таким образом, этот подход может гарантировать вам атомарные операции на уровне ядра. не уровни gcc.
sig_atomic_t является атомарным только по отношению к сигналам. Это потокобезопасный нет.
Начиная с ядра 2.6, стоимость мьютекса почти равна нулю, когда мьютекс свободен. В любом случае, «__sync_lock_test_and_set» (начиная с gcc 4.1) должен помочь, это не единственная функция, которую можно использовать в этом случае.