2010-06-26 2 views
4

Если у меня есть что-то вроде этого ...Использование летучего пока атомное

volatile long something_global = 0; 

long some_public_func() 
{ 
    return something_global++; 
} 

было бы разумно ожидать, этот код не сломаться (состояние гонки) при обращении с несколькими потоками? Если это не стандарт, можно ли это сделать в качестве разумного предположения о современных компиляторах?

ПРИМЕЧАНИЕ: ВСЕ, с помощью которых я использую это, является атомным приращением и декрементом - ничего не интереснее.

ответ

16

Нет - летучий не означает синхронизацию. Это просто означает, что каждый доступ будет возвращать самое современное значение (в отличие от копии, кэшированной локально в потоке).

Post-increment - это не атомная операция, это доступ к памяти, за которым следует запись в память. Перемежение двух может означать, что значение фактически увеличивается один раз.

+0

Что делать, если я использовал preincrement? –

+0

Нет, единственное различие между ними - это какое значение возвращается. Предварительным приращением является также доступ к памяти, за которым следует запись в память. – danben

+4

Aaaaaaaaaaargh. –

3

На современных быстрых многоядерных процессорах есть значительные накладные расходы с атомарными инструкциями из-за кэширования и записи буферов.

Поэтому компиляторы не будут генерировать атомарные инструкции только потому, что вы добавили ключевое слово volatile. Вам необходимо обратиться к встроенным сборкам или расширениям для компилятора (например, gcc atomic builtins).

Я рекомендую использовать библиотеку. Легкий способ - просто сделать блокировку, когда вы хотите обновить переменную. Семафоры, вероятно, будут быстрее, если они будут соответствовать тому, что вы делаете. Кажется, GLib обеспечивает достаточно эффективную реализацию.

+0

Есть ли способ вытащить только атомную библиотеку из GLib? Я бы предпочел не иметь зависимости только для атомистики. –

1

Летучие просто предотвращает оптимизацию, но атомарность требует больше. В x86 для инструкций должен предшествовать префикс LOCK, в MIPS цикл RMW должен быть окружен конструкцией LL/SC, ...

4

Нет, вы должны использовать зависящие от платформы атомные обращения. Есть несколько библиотек, которые их абстрагируют - GLib предоставляет переносные атомные операции, которые при необходимости возвращаются к блокировкам мьютекса, и я считаю, что Boost также предоставляет портативные атомы.

Как я, recently learned, для действительно атомного доступа вам нужен полный барьер памяти, который volatile не предусматривает. Все волатильные гарантии заключаются в том, что память будет перечитываться при каждом доступе и что доступ к volatile памяти не будет переупорядочен. Оптимизатор может повторно заказывать некоторый энергонезависимый доступ до или после изменчивого чтения/записи - возможно, в середине вашего приращения! - поэтому вы должны использовать фактические атомные операции.

+0

Какая библиотека ускорения это? Я не мог найти его (но я, вероятно, просто слеп). –

+0

попробуйте библиотеку Boost.Thread http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html –

+0

@wowus, похоже, что-то в boost.interprocess здесь: http: // Однако www.boost.org/doc/libs/1_43_0/boost/interprocess/detail/atomic.hpp не может найти документы для него. –

3

Windows предоставляет InterlockedIncrementInterlockedDecrement), чтобы делать то, что вы просите.

+0

Что бы я ни искал, кроме того, что мне нужно быть совместимым с Linux. –

+0

Использование TBB - кросс-платформенная (до тех пор, пока она x86/x86_64) надежная атомистика (среди многих других вещей). –

0

Ваша проблема в том, что C не гарантирует атомарность операторов инкремента, и на практике они часто не будут атомарными. Вы должны использовать библиотеку, такую ​​как Windows API или встроенные функции компилятора (GCC, MSVC).

Смежные вопросы