Атомный обмен в GNU C ++ - PullRequest
       64

Атомный обмен в GNU C ++

18 голосов
/ 19 марта 2010

Я хочу убедиться, что мое понимание верно. Подобные вещи хитры, поэтому я почти уверен, что что-то упустил. У меня есть программа, состоящая из потока в реальном времени и потока не в реальном времени. Я хочу, чтобы поток без RT мог поменять местами указатель на память, используемую потоком RT.

Из документации я понимаю, что это может быть достигнуто в g++ с помощью:

// global
Data *rt_data;

Data *swap_data(Data *new_data)
{
#ifdef __GNUC__
    // Atomic pointer swap.
    Data *old_d = __sync_lock_test_and_set(&rt_data, new_data);
#else
    // Non-atomic, cross your fingers.                                          
    Data *old_d = rt_data;
    rt_data = new_data;
#endif
    return old_d;
}

Это единственное место в программе (кроме начальной настройки), где rt_data модифицируется. Когда rt_data используется в контексте реального времени, оно копируется в локальный указатель. Для old_d, позже, когда он уверен, что старая память не используется, она будет освобождена в потоке без RT. Это правильно? Мне нужно volatile где-нибудь? Есть ли другие примитивы синхронизации, которые я должен вызывать?

Кстати, я делаю это в C ++, хотя меня интересует, отличается ли ответ для C.

Спасибо заранее.

Ответы [ 2 ]

25 голосов
/ 19 марта 2010

Как правило, не используйте volatile при написании параллельного кода в C/C++. Семантика volatile настолько близка к тому, что вы хотите, что это заманчиво, но в итоге volatile недостаточно . К сожалению Java/C# volatile != C/C++ volatile. У Херба Саттера есть отличная статья , объясняющая запутанный беспорядок.

То, что вы действительно хотите, это забор памяти. __sync_lock_test_and_set обеспечивает ограждение для вас.

Вам также понадобится ограничитель памяти при копировании (загрузке) указателя rt_data на локальную копию.

Программирование без блокировки сложно. Если вы хотите использовать расширения Gcc c ++ 0x, это немного проще:

#include <cstdatomic>

std::atomic<Data*> rt_data;

Data* swap_data( Data* new_data )
{
   Data* old_data = rt_data.exchange(new_data);
   assert( old_data != new_data );
   return old_data;
}

void use_data( )
{
   Data* local = rt_data.load();
   /* ... */
}
3 голосов
/ 19 марта 2010

Обновление : Этот ответ неверен, так как мне не хватает того факта, что volatile гарантирует, что доступ к volatile переменным не переупорядочен, но не предоставляет таких гарантий в отношении других volatile доступы и манипуляции. Забор памяти предоставляет такие гарантии и необходим для этого применения. Мой оригинальный ответ ниже, но не действуйте на него. См. этот ответ для хорошего объяснения в дыре в моем понимании, которая привела к следующему неправильному ответу.

Оригинальный ответ:

Да, вам нужно volatile в вашей декларации rt_data; каждый раз, когда переменная может быть изменена вне потока управления потока, обращающегося к ней, она должна быть объявлена ​​volatile. Хотя вы можете обойтись без volatile, так как вы копируете в локальный указатель, volatile, по крайней мере, помогает с документацией, а также запрещает некоторые оптимизации компилятора, которые могут вызвать проблемы. Рассмотрим следующий пример, принятый из DDJ :

volatile int a;
int b;
a = 1;
b = a;

Если для a возможно изменить его значение между a=1 и b=a, тогда a должно быть объявлено volatile (если, конечно, не присвоено устаревшее значение b приемлемо). Многопоточность, особенно с атомарными примитивами, представляет собой такую ​​ситуацию. Ситуация также инициируется переменными, измененными обработчиками сигналов и переменными, отображаемыми в нечетные ячейки памяти (например, аппаратные регистры ввода / вывода). Смотри также этот вопрос .

В остальном это выглядит нормально для меня.

В C я бы, вероятно, использовал для этого атомарные примитивы, предоставляемые GLib . Они будут использовать атомарную операцию там, где она доступна, и прибегнут к медленной, но правильной реализации на основе мьютекса, если атомарные операции недоступны. Boost может обеспечить нечто подобное для C ++.

...