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ść.
c++
multithreading
synchronization
theschmitzer
źródło
źródło
=
: stackoverflow.com/questions/8290768/…Odpowiedzi:
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 .
źródło
Chłopcze, co za pytanie. Odpowiedź brzmi:
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.
źródło
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.
źródło
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.
źródło
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.
źródło
Gwarantujemy, że w systemie Windows Interlocked *** Exchange *** Add jest atomowy.
źródło
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.
źródło
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ń ).
źródło
Zgadzam się z wieloma, a zwłaszcza z Jasonem . W systemie Windows prawdopodobnie użyłbyś InterlockedAdd i jego przyjaciół.
źródło
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ć.
źródło
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”.
źródło
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.
źródło
x+=6
.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 ++
źródło
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.
źródło
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.
źródło
arithmetic
operacja, która składa się z sekwencji odczytu zmiana zapisu w „zwykłych” zmiennych nie są niepodzielne, a nie czyread
lubwrite
operacji na zmiennych „zwykłych” są atomowych lub nie.