Czy C ++ odczytuje i zapisuje int Atomic?

81

Mam dwa wątki, jeden aktualizujący int, a drugi czytający. Jest to wartość statystyczna, w przypadku której kolejność odczytów i zapisów nie ma znaczenia.

Moje pytanie brzmi, czy mimo wszystko muszę zsynchronizować dostęp do tej wartości wielobajtowej? Innymi słowy, część zapisu może zostać zakończona i przerwana, a następnie nastąpi odczyt.

Na przykład pomyśl o wartości = 0x0000FFFF, która otrzymuje zwiększoną wartość 0x00010000.

Czy jest czas, w którym wartość wygląda jak 0x0001FFFF, o który powinienem się martwić? Z pewnością im większy typ, tym bardziej prawdopodobne jest, że coś takiego może się wydarzyć.

Zawsze synchronizowałem tego typu dostępy, ale byłem ciekawy, co myśli społeczność.

theschmitzer
źródło
5
Naprawdę? Nie obchodzi mnie, co myśli społeczność.
Zależy
1
Ciekawa lektura na ten temat: channel9.msdn.com/Shows/Going+Deep/…
ereOn
W szczególności dla =: stackoverflow.com/questions/8290768/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

47

Na początku można by pomyśleć, że odczyty i zapisy rozmiaru maszyny natywnej są atomowe, ale istnieje wiele problemów, którymi należy się zająć, w tym spójność pamięci podręcznej między procesorami / rdzeniami. Użyj operacji atomowych, takich jak Interlocked * w systemie Windows i ich odpowiedników w systemie Linux. C ++ 0x będzie miał „atomowy” szablon do umieszczenia ich w ładnym i wieloplatformowym interfejsie. Na razie, jeśli używasz warstwy abstrakcji platformy, może ona zapewniać te funkcje. ACE tak, zobacz szablon klasy ACE_Atomic_Op .

Adam Mitz
źródło
Dokument ACE_Atomic_Op został przeniesiony - można go teraz znaleźć pod adresem dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/Atomic_Op.inl
Byron
63

Chłopcze, co za pytanie. Odpowiedź brzmi:

Tak, nie, hmmm, to zależy

Wszystko sprowadza się do architektury systemu. Na IA32 poprawnie wyrównany adres będzie operacją atomową. Niewyrównane zapisy mogą być atomowe, zależy to od używanego systemu buforowania. Jeśli pamięć znajduje się w pojedynczej linii pamięci podręcznej L1, to jest atomowa, w przeciwnym razie nie. Szerokość magistrali między procesorem a pamięcią RAM może wpływać na atomową naturę: prawidłowo wyrównany 16-bitowy zapis na 8086 był atomowy, podczas gdy ten sam zapis na 8088 nie był, ponieważ 8088 miał tylko 8-bitową magistralę, podczas gdy 8086 miał 16-bitowa magistrala.

Ponadto, jeśli używasz C / C ++, nie zapomnij oznaczyć współdzielonej wartości jako niestabilnej, w przeciwnym razie optymalizator uzna, że ​​zmienna nigdy nie zostanie zaktualizowana w jednym z twoich wątków.

Skizz
źródło
23
5
@IngeHenriksen: Nie przekonuje mnie ten link.
Skizz,
inne źródło, ale niestety bardzo stare (poprzedza std :: atomic): web.archive.org/web/20190219170904/https://software.intel.com/…
Max Barraclough
11

JEŚLI czytasz / piszesz 4-bajtową wartość ORAZ jest ona wyrównana do DWORD w pamięci ORAZ pracujesz na architekturze I32, WTEDY odczyty i zapisy są atomowe.

gabr
źródło
2
Gdzie w podręcznikach dla programistów architektury Intel jest to zaznaczone?
Daniel Trebbien,
2
@DanielTrebbien: może zobaczyć stackoverflow.com/questions/5002046/...
sehe
9

Tak, musisz zsynchronizować dostęp. W C ++ 0x będzie to wyścig danych i niezdefiniowane zachowanie. W wątkach POSIX jest to już niezdefiniowane zachowanie.

W praktyce możesz otrzymać złe wartości, jeśli typ danych jest większy niż rodzimy rozmiar słowa. Ponadto inny wątek może nigdy nie zobaczyć zapisanej wartości z powodu optymalizacji przenoszącej odczyt i / lub zapis.

Anthony Williams
źródło
3

Musisz zsynchronizować, ale na niektórych architekturach są na to wydajne sposoby.

Najlepiej jest używać podprogramów (być może ukrytych za makrami), aby można było warunkowo zastąpić implementacje specyficznymi dla platformy.

Jądro Linuksa ma już część tego kodu.

Jason Cohen
źródło
3

Gwarantujemy, że w systemie Windows Interlocked *** Exchange *** Add jest atomowy.

Andrew Stein
źródło
2

Aby powtórzyć to, co wszyscy mówili na górze, język sprzed C ++ 0x nie gwarantuje niczego w zakresie dostępu do pamięci współdzielonej z wielu wątków. Wszelkie gwarancje zależałyby od kompilatora.

Tim Cooper
źródło
0

Nie, nie są (a przynajmniej nie możesz zakładać, że tak jest). Powiedziawszy to, istnieją pewne sztuczki, które pozwalają zrobić to niepodzielnie, ale zazwyczaj nie są one przenośne (zobacz Porównaj i zamień ).

Leon Timmermans
źródło
0

Zgadzam się z wieloma, a zwłaszcza z Jasonem . W systemie Windows prawdopodobnie użyłbyś InterlockedAdd i jego przyjaciół.

Kenny
źródło
0

Oprócz wspomnianego wyżej problemu z pamięcią podręczną ...

Jeśli przeniesiesz kod do procesora o mniejszym rozmiarze rejestru, nie będzie on już atomowy.

IMO, problemy z wątkami są zbyt trudne, aby ryzykować.

JeffV
źródło
0

Jedynym przenośnym sposobem jest użycie typu sig_atomic_t zdefiniowanego w nagłówku signal.h dla kompilatora. W większości implementacji C i C ++ jest to int. Następnie zadeklaruj swoją zmienną jako „volatile sig_atomic_t”.

Edward Howorka
źródło
niestabilny nie robi tego, co myślisz, że robi stackoverflow.com/questions/2484980/ ...
Sam Miller
0

Weźmy ten przykład

int x;
x++;
x=x+5;

Zakłada się, że pierwsza instrukcja jest atomowa, ponieważ przekłada się na pojedynczą dyrektywę zestawu INC, która zajmuje jeden cykl procesora. Jednak drugie przypisanie wymaga kilku operacji, więc wyraźnie nie jest to operacja atomowa.

Inny np.

x=5;

Ponownie, musisz zdemontować kod, aby zobaczyć, co dokładnie się tutaj dzieje.

siddhusingh
źródło
2
Ale kompilator może go zoptymalizować do x+=6.
tc.
0

tc, myślę, że w momencie, gdy użyjesz stałej (np. 6), instrukcja nie zostanie ukończona w jednym cyklu maszyny. Spróbuj zobaczyć zestaw instrukcji x + = 6 w porównaniu z x ++

siddhusingh
źródło
0

Niektórzy myślą, że ++ c jest atomowe, ale mają oko na wygenerowany asembler. Na przykład z „gcc -S”:

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

Aby zwiększyć liczbę int, kompilator najpierw ładuje ją do rejestru i zapisuje z powrotem w pamięci. To nie jest atomowe.

etham
źródło
1
Nie stanowi to problemu, jeśli tylko jeden wątek zapisuje do zmiennej, ponieważ nie ma przerwania.
Ben Voigt,
0

Zdecydowanie NIE! Ta odpowiedź od naszego najwyższego autorytetu w C ++, M. Boost:
Nie ma gwarancji, że operacje na „zwykłych” zmiennych będą atomowe.

Jean Davy
źródło
2
linkujące tylko mówi arithmeticoperacja, która składa się z sekwencji odczytu zmiana zapisu w „zwykłych” zmiennych nie są niepodzielne, a nie czy readlub writeoperacji na zmiennych „zwykłych” są atomowych lub nie.
D3Hunter