2015-11-15 16 views
5

Piszę konstruktora kopiowania dla struktury danych, która musi skopiować dwa elementy std::atomic<T> do nowego obiektu. Chociaż proces ten niekoniecznie musi być atomowy w moim przypadku użycia, wolałbym mieć możliwie najbardziej poprawne rozwiązanie.Nonlocking Way to Copy Atomics in Copy Constructor

Jestem świadomy, że konstruktor kopii został jawnie usunięty z std::atomic<T>, aby zmusić użytkowników do korzystania z interfejsu atomowego.

atomowa (stała atomowa &) = usuń;

Co Ja obecnie robie tak:

SomeObject(const SomeObject& other): 
    _atomic1(other._atomic1.load()),    
    _atomic2(other._atomic2.load()) { 
... 
} 

Nie wierzę, ta operacja jest atomowa, ani nie znam sposób, aby to tak (bez zamków).

Czy istnieje sposób na kopiowanie tych wartości atomowo (bez blokad)?

+0

Nie ma ogólnego sposobu atomowego kopiowania dwóch losowych obiektów atomowych, jeśli o to pytasz. Dodaj muteksa, jeśli rzeczywiście potrzebujesz kopii atomowej. –

+3

9 razy na 10, odpowiedź na to pytanie będzie brzmiała: "nie musisz tego robić, robisz wszystko, co robisz w niewłaściwy sposób". Jeśli struktura danych jest na przykład zbiorem, to będzie już miała sposób na uzyskanie jej zawartości w sposób bezpieczny dla współbieżności i powinieneś go użyć. –

+0

@MattTimmermans Jest to świetny punkt, chociaż moje pytanie ma charakter akademicki. ** Widzowie tego pytania powinni wziąć pod uwagę i używać typów zaprojektowanych przez ekspertów, jeśli to możliwe. ** Współbieżność jest trudna, więc jeśli napiszesz coś sam, sprawdź ją. –

Odpowiedz

4

Jedynym sposobem jest utworzenie banalnie kopiowalnej struktury S zawierającej dwa T s i użycie std::atomic<S>.

Zauważ, że działa to tylko wtedy, gdy używasz tego S od samego początku - nie ma sposobu na atomowe ładowanie dwóch oddzielnych atomów bez blokad.

Więc zamiast:

struct SomeObject { 
    SomeObject(const SomeObject& other) : i(other.i.load()), j(other.j.load()) { } 
    std::atomic<int> i, j; 
}; 

Wykonaj:

struct SomeObject { 
    SomeObject(const SomeObject& other) : data(other.data.load()) { } 
    struct Data { int i, j; }; 
    std::atomic<Data> data; 
}; 

Należy pamiętać, że może to (najprawdopodobniej) nadal korzystać z blokady wewnętrznie. Użyj is_lock_free, aby sprawdzić, czy to robi.

+0

Hmm. To twórcze, ale podejrzewam, że używa wewnętrznych zamków. Wypróbuję to. –

+0

@DonScott [Dla <= 64-bitowych struktur wydaje się być bez blokady, ale nie większy.] (Http://coliru.stacked-crooked.com/a/e496ba55f9a710a8) – orlp

+0

Mam taki sam wynik, ale widzowie powinni pamiętać, że ** to zachowanie zależy od kompilatora i architektury. ** Wyrównane odczytanie/zapisanie 8 bajtów jest generalnie atomiczne na najnowszym sprzęcie (nawet w 32-bitowych systemach operacyjnych), ale użytkownicy powinni zadbać o sprawdzenie swoich systemów. –