Атомарная подкачка в GNU C++

Я хочу проверить, что мое понимание корректно. Такого рода вещь хитра, таким образом, я почти уверен, что пропускаю что-то. У меня есть программа, состоящая из потока в реальном времени и потока нев реальном времени. Я хочу, чтобы поток не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.

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

18
задан Steve 19 March 2010 в 15:15
поделиться

2 ответа

Обычно не используйте 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();
   /* ... */
}
25
ответ дан 30 November 2019 в 08:15
поделиться

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

Исходный ответ:

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

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

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

В остальном, мне кажется, это нормально.

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

3
ответ дан 30 November 2019 в 08:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: