2014-11-21 3 views
14

Первый вопрос: есть ли способ доступа к членам структуры в объекте atomic<struct>? Например, я получаю ошибку компиляции:Непонимание атомных структур и указателей

struct std::atomic<node>’ has no member named ‘data’ a.data = 0; 

в этом сегменте

struct node{ 
    int data; 
    node* next; 
}; 

int main(){ 
    atomic<node> a; 
    a.data = 0; 
} 

Я могу работать вокруг него, создавая временный узел, как так:

atomic<node> a; 
    node temp; 
    temp.data = 0; 
    a.store(temp); 

но это Безразлично Кажется очень элегантным.

Второй вопрос: что, если у меня есть указатель на атомный объект? Есть ли вообще доступ к элементам узла напрямую? Очевидно, что следующее не компилируется, как бы изменить это, чтобы сохранить 0 в значении узла в точке b?

atomic<node> b = new node; 
b->data = 0; 

Это решение, которое я нашел, но опять же, есть ли более элегантный способ сделать это?

atomic<node> *b; 
node temp; 
temp.data = 0; 
b->store(&temp); 

И, наконец, в чем разница между atomic<node*> и atomic<node>*

+0

Нет, существует только ограниченный набор атомных операций (загрузка, хранение, обмен, ...). –

+1

'atomic ' обеспечивает атомное обновление указателя, который он удерживает (не то, на что указывает указатель, указатель). 'atomic *' - указатель на атомный ', целью которого является принудительное атомное обновление объекта' node'. – Barry

+2

Если вам нужна структура, в которой вы можете атомически обновлять * оба * члена вместе или отдельно атомарно изменять один из них (без compare_exchange_weak на всей структуре), вы можете использовать [объединение атомной структуры и структуры с двумя атомарными члены] (http://stackoverflow.com/questions/38984153/implement-aba-counter-with-c11-cas/38991835#38991835). (Если вы используете компилятор C++, который гарантирует, что запись одного члена объединения, а затем чтение другого - это нормально, как в C99). Это фактически работает (эффективно) для структур с максимальным размером, которое может иметь аппаратное обеспечение cmpxchg, то есть 16B на x86-64. –

ответ

9

this [workaround] doesn't seem very elegant.

std::atomic<T> не может сделать произвольные операции атомарным только загрузка и хранение данных поддерживается. Вот почему ваш «обходной путь» на самом деле является способом обращения с атомными объектами: вы готовите новое значение node любым удобным вам способом, а затем атомизировали его в переменную atomic<node>.

what if I have a pointer to an atomic object? Is there anyway to access the members of the node directly?

доступ к содержимому узла через указатель не будет атомным, а также: поскольку std::atomic<T> может гарантировать только загрузку и хранение его значению атомарными, он не позволяет получить доступ к T «s членам, не делая явная копия. Это хорошо, потому что это мешает читателям кода получить ложное впечатление о том, что доступ к внутренним элементам T является чем-то атомным.

what is the difference between atomic<node*> and atomic<node>*

В случае елей, атомный объект сохраняет указатель, которые могут быть доступны атомарно (то есть вы можете повторно точку, этот указатель на новый узел атомарно). Во втором случае атомный объект сохраняет значение, доступ к которому можно получить атомарно, что означает, что вы можете читать и писать все node атомарно.

+1

Итак, если бы я хотел внести изменения в член, то произойдет атомарно, я должен сделать элемент атомом, а не структурой? –

+1

@MattPennington Точно!Если вы хотите, чтобы доступ к данным внутри 'node' был атомарным, ваш узел выглядел бы следующим образом:' struct node {atomic data;}; ' – dasblinkenlight

+0

Пока вы в порядке, обременяя всех, кто использует вашу структуру при любых обстоятельствах, атомный. Если вас беспокоит проблема безопасности потоков в нескольких конкретных контекстах, лучше всего использовать отдельный мьютекс. – dlf

2

Когда вы

atomic<node> a; 
node temp; // use a.load() to copy all the fields of a to temp 
temp.data = 0; 
a.store(temp); 

вы потеряете значение следующего поля. Я бы предложил предложенное изменение. Если бы узел был бы простого типа, например std :: atomic_int, я думаю, что использование оператора «=» было бы возможно. В противном случае нет. Я не думаю, что есть еще один способ обхода вашего дела.

And lastly, what is the difference between atomic < node* > and atomic < node > *?

При использовании атомных < узла *> операция, проведенной на адрес объекта узла будет атомными в то время как в другом случае вам нужно выделить память для атомного объекта и операция делаются на фактический объект узла будет атомарным.

1

Обратите внимание, что ваше «решение» включает в себя неатомное чтение-изменение-запись всех членов, кроме .data.

atomic<node> a; 

node temp = a.load(); 
temp.data = 0; 
a.store(temp); // steps on any changes to other member that happened after our load 

Если вы хотите-структуру, где вы можете атомарно обновить все членов вместе или по отдельности атомарно модифицировать один из них (без compare_exchange_weak на всей структуры), вы можете использовать a union of an atomic struct and a struct with two atomic members. Это может быть полезно для, например, оба указателя в двойном списке или указатель + счетчик. Текущие компиляторы плохи даже при чтении одного члена атомной структуры, не делая что-то медленное, как использование CMPXCHG16B для загрузки всей структуры, а затем просто посмотрите на один элемент. (Так обстоит дело с gcc6.2 даже с memory_order_relaxed).

Этот профсоюзный взлом работает только в том случае, если вы используете компилятор C++, который гарантирует, что запись одного члена объединения, а затем чтение другого в порядке, как на C99.

Это работает для структур с максимальным размером аппаратного обеспечения cmpxchg, то есть 16B на x86-64 (если вы включите -mcx16 в gcc, используйте CMPXCHG16B, которые не поддерживали первое поколение процессоров K8, поэтому это не технически базовый уровень x86-64).

Для больших структур atomic<the_whole_thing> не будет заблокирован, а чтение/письмо членам его через atomic<int> в другом члене профсоюза не будет безопасным. Однако чтение может быть в порядке.

Это может вызвать беспорядок семантики упорядочения памяти, поскольку даже сильно упорядоченный x86 может reorder a narrow store with a wider load that fully contains it. Если вам в основном просто нужна атомарность, это здорово, но чтение полного объекта (например, при выполнении cmpxchg) в том же потоке, который только что написал один член, требует MFENCE на x86 даже для семантики получения/выпуска. Вы всегда будете видеть свой собственный магазин, но если другие потоки хранятся на одном и том же объекте, они могут наблюдать за вашим магазином, как это происходит после загрузки.

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