Czy to oznacza, że dwa wątki nie mogą jednocześnie zmienić podstawowych danych? Czy też oznacza to, że dany segment kodu będzie działał z przewidywalnymi wynikami, gdy wiele wątków wykonuje ten segment kodu?
multithreading
language-agnostic
programming-languages
concurrency
Varun Mahajan
źródło
źródło
Odpowiedzi:
Z Wikipedii:
Czytaj więcej:
http://en.wikipedia.org/wiki/Thread_safety
w języku niemieckim: http://de.wikipedia.org/wiki/Threadsicherheit
w języku francuskim: http://fr.wikipedia.org/wiki/Threadsafe (bardzo krótko)
źródło
Kod bezpieczny dla wątków to kod, który działa, nawet jeśli wiele wątków wykonuje go jednocześnie.
http://mindprod.com/jgloss/threadsafe.html
źródło
Bardziej pouczające pytanie dotyczy tego, co sprawia, że kod nie jest bezpieczny dla wątków, a odpowiedź jest taka, że muszą być spełnione cztery warunki ... Wyobraź sobie następujący kod (i jego tłumaczenie na język maszynowy)
źródło
Podoba mi się definicja z Briana Goetza z Java Concurrency in Practice ze względu na jej kompleksowość
„Klasa jest bezpieczna dla wątków, jeśli zachowuje się poprawnie, gdy uzyskuje się do niej dostęp z wielu wątków, niezależnie od planowania lub przeplatania wykonywania tych wątków przez środowisko wykonawcze i bez dodatkowej synchronizacji lub innej koordynacji ze strony kodu wywołującego. „
źródło
Jak zauważyli inni, bezpieczeństwo wątków oznacza, że fragment kodu będzie działał bez błędów, jeśli użyje go więcej niż jeden wątek jednocześnie.
Warto mieć świadomość, że czasami wiąże się to z kosztem czasu komputerowego i bardziej złożonego kodowania, więc nie zawsze jest to pożądane. Jeśli klasa może być bezpiecznie użyta tylko w jednym wątku, lepiej to zrobić.
Na przykład Java ma dwie klasy, które są prawie równoważne,
StringBuffer
iStringBuilder
. Różnica polega na tym, żeStringBuffer
jest wątkowa, więc jedno wystąpienieStringBuffer
może być używane przez wiele wątków jednocześnie.StringBuilder
nie jest bezpieczny dla wątków i został zaprojektowany jako wydajniejszy zamiennik dla tych przypadków (zdecydowana większość), gdy Łańcuch jest zbudowany tylko z jednego wątku.źródło
Kod bezpieczny dla wątku działa zgodnie ze specyfikacją, nawet jeśli jest wprowadzany jednocześnie przez różne wątki. Często oznacza to, że wewnętrzne struktury danych lub operacje, które powinny działać nieprzerwanie, są chronione przed różnymi modyfikacjami jednocześnie.
źródło
Łatwiejszym sposobem na zrozumienie tego jest to, że kod nie jest bezpieczny dla wątków. Istnieją dwa główne problemy, które powodują, że aplikacja wątkowa ma niepożądane zachowanie.
Dostęp do zmiennej współdzielonej bez blokowania
Ta zmienna może być modyfikowana przez inny wątek podczas wykonywania funkcji. Chcesz temu zapobiec za pomocą mechanizmu blokującego, aby mieć pewność, że Twoja funkcja się zachowa. Ogólna zasada polega na utrzymywaniu blokady przez możliwie najkrótszy czas.
Zakleszczenie spowodowane wzajemną zależnością od zmiennej współdzielonej
Jeśli masz dwie zmienne współdzielone A i B. W jednej funkcji najpierw blokujesz A, a następnie blokujesz B. W innej funkcji zaczynasz blokować B, a po pewnym czasie blokujesz A. jest potencjalnym impasem, w którym pierwsza funkcja będzie czekać na odblokowanie B, gdy druga funkcja będzie czekać na odblokowanie A. Ten problem prawdopodobnie nie wystąpi w twoim środowisku programistycznym i tylko od czasu do czasu. Aby tego uniknąć, wszystkie zamki muszą zawsze być w tej samej kolejności.
źródło
Tak i nie.
Bezpieczeństwo wątków to coś więcej niż tylko upewnienie się, że do twoich udostępnionych danych ma dostęp tylko jeden wątek na raz. Musisz zapewnić sekwencyjny dostęp do współużytkowanych danych, jednocześnie unikając warunków wyścigu , impasu , blokad żywych i głodu zasobów .
Nieprzewidywalne wyniki, gdy działa wiele wątków, nie są wymaganym warunkiem kodu zabezpieczającego wątki, ale często są produktem ubocznym. Na przykład możesz mieć schemat producent-konsument skonfigurowany ze wspólną kolejką, jednym wątkiem producenta i kilkoma wątkami konsumenta, a przepływ danych może być doskonale przewidywalny. Jeśli zaczniesz wprowadzać więcej klientów, zobaczysz więcej losowo wyglądających wyników.
źródło
Zasadniczo wiele rzeczy może pójść nie tak w środowisku wielowątkowym (zmiana kolejności instrukcji, częściowo skonstruowane obiekty, ta sama zmienna o różnych wartościach w różnych wątkach z powodu buforowania na poziomie procesora itp.).
Podoba mi się definicja podana przez Java Concurrency in Practice :
Przez prawidłowe oznaczają, że program zachowuje się zgodnie ze specyfikacjami.
Wymyślony przykład
Wyobraź sobie, że wdrażasz licznik. Można powiedzieć, że zachowuje się poprawnie, jeśli:
counter.next()
nigdy nie zwraca wartości, która została już wcześniej zwrócona (dla uproszczenia nie zakładamy przepełnienia itp.)Licznik bezpieczny dla wątków działałby zgodnie z tymi regułami bez względu na to, ile wątków uzyskuje dostęp do niego jednocześnie (co zwykle nie byłoby przypadkiem naiwnej implementacji).
Uwaga: cross-post dla programistów
źródło
Po prostu - kod będzie działał poprawnie, jeśli wiele wątków wykonuje ten kod w tym samym czasie.
źródło
Nie myl bezpieczeństwa nici z determinizmem. Kod bezpieczny dla wątków może być również niedeterministyczny. Biorąc pod uwagę trudność debugowania problemów z kodem wątkowym, jest to prawdopodobnie normalny przypadek. :-)
Bezpieczeństwo wątków zapewnia po prostu, że gdy wątek modyfikuje lub odczytuje udostępnione dane, żaden inny wątek nie może uzyskać do niego dostępu w sposób, który zmienia dane. Jeśli Twój kod zależy od określonej kolejności wykonania dla poprawności, potrzebujesz innych mechanizmów synchronizacji poza tymi wymaganymi dla bezpieczeństwa wątków, aby to zapewnić.
źródło
Chciałbym dodać więcej informacji na temat innych dobrych odpowiedzi.
Bezpieczeństwo wątków oznacza, że wiele wątków może zapisywać / odczytywać dane w tym samym obiekcie bez błędów niespójności pamięci. W programie wielowątkowym program bezpieczny w wątku nie powoduje skutków ubocznych w udostępnianych danych .
Więcej informacji na ten temat SE:
Co znaczy Threadafe?
Program bezpieczny dla wątków gwarantuje spójność pamięci .
Ze strony dokumentacji Oracle w zaawansowanym współbieżnym interfejsie API:
Właściwości spójności pamięci:
Rozdział 17 specyfikacji języka Java ™ definiuje relację „dzieje się przed” operacjami pamięci, takimi jak odczyty i zapisy zmiennych współużytkowanych. Gwarantuje to, że wyniki zapisu przez jeden wątek będą widoczne dla odczytu przez inny wątek tylko wtedy, gdy nastąpi operacja zapisu przed operacją odczytu .
Konstrukty
synchronized
ivolatile
, a także metodyThread.start()
iThread.join()
, mogą tworzyć relacje przed wydarzeniami.Metody wszystkich klas
java.util.concurrent
i podpakietów rozszerzają te gwarancje na synchronizację wyższego poziomu. W szczególności:Runnable
zdarzenia do -Executor
zanim rozpocznie się jego wykonanie. Podobnie w przypadku Callables przesłanych doExecutorService
.Future
działania poprzedzające po odzyskaniu wynikuFuture.get()
w innym wątku.Lock.unlock, Semaphore.release, and CountDownLatch.countDown
działania wykonywane przed udaną metodą „akwizycji”, takie jakLock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
na tym samym obiekcie synchronizacyjnym w innym wątku.Exchanger
, mają miejsce akcje poprzedzająceexchange()
w każdym wątku - przed działaniami następującymi po odpowiedniej wymianie () w innym wątku.CyclicBarrier.await
iPhaser.awaitAdvance
(jak również jego warianty) zdarzają się - przed akcjami wykonywanymi przez akcję barierową, a akcje wykonywane przez akcję barierową - przed czynnościami po udanym powrocie z odpowiedniego oczekiwania czekają w innych wątkach.źródło
Aby wypełnić inne odpowiedzi:
Synchronizacja to tylko zmartwienie, gdy kod w metodzie robi jedną z dwóch rzeczy:
Oznacza to, że zmienne zdefiniowane W twojej metodzie są zawsze wątkowo bezpieczne. Każde wywołanie metody ma własną wersję tych zmiennych. Jeśli metoda jest wywoływana przez inny wątek lub przez ten sam wątek, lub nawet jeśli metoda wywołuje się sama (rekurencja), wartości tych zmiennych nie są współużytkowane.
Nie można zagwarantować, że planowanie wątków będzie działać w trybie round-robin . Zadanie może całkowicie pochłonąć procesor kosztem wątków o tym samym priorytecie. Możesz użyć Thread.yield (), aby mieć sumienie. Możesz użyć (w java) Thread.setPriority (Thread.NORM_PRIORITY-1), aby obniżyć priorytet wątku
Dodatkowo uważaj na:
źródło
Tak i tak. Oznacza to, że dane nie są modyfikowane jednocześnie przez więcej niż jeden wątek. Jednak Twój program może działać zgodnie z oczekiwaniami i wydawać się bezpieczny dla wątków, nawet jeśli zasadniczo nie jest.
Należy pamiętać, że nieprzewidywalność wyników jest konsekwencją „warunków wyścigu”, które prawdopodobnie skutkują modyfikacją danych w kolejności innej niż oczekiwana.
źródło
Odpowiedzmy na ten przykład:
countTo10
Sposób dodaje jeden do licznika, a następnie zwraca wartość true, jeżeli liczba osiągnęła 10. Należy zwrócić tylko raz prawdziwe.Działa to tak długo, jak długo tylko jeden wątek uruchamia kod. Jeśli dwa wątki uruchamiają kod w tym samym czasie, mogą wystąpić różne problemy.
Na przykład, jeśli liczenie zaczyna się od 9, jeden wątek może dodać 1 do zliczenia (co 10), ale następnie drugi wątek może wejść do metody i dodać 1 ponownie (co 11), zanim pierwszy wątek będzie miał szansę wykonać porównanie z 10 Następnie oba wątki dokonują porównania i stwierdzają, że liczba wynosi 11 i żaden nie zwraca wartości true.
Więc ten kod nie jest bezpieczny dla wątków.
Zasadniczo wszystkie problemy związane z wielowątkowością są spowodowane pewną odmianą tego rodzaju problemu.
Rozwiązaniem jest zapewnienie, aby dodanie i porównanie nie mogły być oddzielone (na przykład przez otoczenie dwóch instrukcji jakimś kodem synchronizacyjnym) lub przez opracowanie rozwiązania, które nie wymaga dwóch operacji. Taki kod byłby bezpieczny dla wątków.
źródło
Przynajmniej w C ++ myślę, że bezpieczny w wątkach jest trochę mylący, ponieważ pozostawia wiele poza nazwą. Aby być bezpiecznym dla wątków, kod zazwyczaj musi być proaktywny . Na ogół nie jest to cecha pasywna.
Aby klasa była bezpieczna dla bieżnika, musi mieć „dodatkowe” funkcje, które zwiększają obciążenie. Funkcje te są częścią implementacji klasy i ogólnie mówiąc, ukryte przed interfejsem. Oznacza to, że różne wątki mogą uzyskiwać dostęp do dowolnego członka klasy bez konieczności martwienia się o konflikt z dostępem równoległym przez inny wątek ORAZ mogą to robić w bardzo leniwy sposób, używając zwykłego starego, normalnego stylu kodowania, bez konieczności robienia tego wszystkie te szalone rzeczy związane z synchronizacją, które są już wbudowane w wnętrzności wywoływanego kodu.
I dlatego niektórzy ludzie wolą używać tego terminu zsynchronizowanego wewnętrznie .
Zestawy terminologiczne
Istnieją trzy główne zestawy terminologii dotyczącej tych pomysłów, które napotkałem. Pierwszą i historycznie bardziej popularną (ale gorszą) jest:
Drugi (i lepszy) to:
Trzeci to:
Analogie
wątek bezpieczny ~ dowód wątku ~ zsynchronizowany wewnętrznie
Przykładem systemu zsynchronizowanego wewnętrznie (znanego również jako wątek bezpieczny lub odporny na gwint ) jest restauracja, w której gospodarz wita cię przy drzwiach i uniemożliwia kolejkowanie. Gospodarz jest częścią mechanizmu restauracji służącego do radzenia sobie z wieloma klientami i może zastosować pewne trudne sztuczki w celu optymalizacji miejsc siedzących oczekujących klientów, na przykład biorąc pod uwagę wielkość imprezy lub czas, na jaki wyglądają , a nawet przyjmowanie rezerwacji przez telefon. Restauracja jest wewnętrznie zsynchronizowana, ponieważ wszystko to jest częścią interfejsu do interakcji z nią.
nie jest bezpieczny (ale fajny) ~ kompatybilny z wątkiem ~ zsynchronizowany zewnętrznie ~ z wolnym wątkiem
Załóżmy, że idziesz do banku. Istnieje linia, czyli spór dla kasjerów bankowych. Ponieważ nie jesteś dzikim, zdajesz sobie sprawę, że najlepszą rzeczą, jaką można zrobić w trakcie rywalizacji o zasoby, jest stanie w kolejce jak cywilizowana istota. Nikt technicznie tego nie zmusza. Mamy nadzieję, że masz niezbędne programy społecznościowe, aby zrobić to sam. W tym sensie lobby bankowe jest zsynchronizowane zewnętrznie. Czy powinniśmy powiedzieć, że jest to niebezpieczne? to implikuje to, jeśli korzystasz z zestawu terminologii bipolarnej bezpiecznej dla wątku , która nie jest bezpieczna dla wątku . To niezbyt dobry zestaw warunków. Lepsza terminologia to zsynchronizowana zewnętrznie,Lobby bankowe nie jest wrogie dla wielu klientów, ale też nie synchronizuje ich. Klienci robią to sami.
Nazywa się to również „wolnym wątkiem”, gdzie „wolny” jest jak w „wolnym od wszy” - lub w tym przypadku zamki. Dokładniej mówiąc, prymitywy synchronizacji. To nie znaczy, że kod może działać na wielu wątkach bez tych prymitywów. Oznacza to po prostu, że nie ma go już zainstalowanego i to Ty, użytkownik kodu, musisz zainstalować je sam, jak uznasz za stosowne. Instalowanie własnych prymitywów synchronizacji może być trudne i wymaga intensywnego myślenia o kodzie, ale może także prowadzić do najszybszego możliwego programu, umożliwiając dostosowanie sposobu działania programu na dzisiejszych procesorach z przeplotem.
nie wątek bezpieczny (i zły) ~ wątek wrogi ~ niezsynchronizowany
Przykładem codziennej analogii do systemu wrogiego wątkom jest szarpnięcie samochodu sportowego, który odmawia korzystania z kierunkowskazów i nie chce zmieniać pasów. Ich styl jazdy jest wrogi gwint lub unsychronizable ponieważ nie ma sposobu, aby skoordynować z nimi, a to może prowadzić do rywalizacji na tym samym pasie ruchu, bez rezolucji, a tym samym przypadkiem jak dwa samochody próbować zajmować tę samą przestrzeń, bez protokołu do zapobiec temu. Ten wzorzec można również traktować szerzej jako antyspołeczny, co wolę, ponieważ jest mniej specyficzny dla wątków, a więc bardziej ogólnie dotyczy wielu dziedzin programowania.
Dlaczego Thread Safe i in. są złym zestawem terminologii
Pierwszy i najstarszy zestaw terminologii nie czyni dokładniejszego rozróżnienia między wrogością wątków a kompatybilnością wątków . Kompatybilność wątków jest bardziej pasywna niż tak zwane bezpieczeństwo wątków, ale to nie znaczy, że wywoływany kod jest niebezpieczny dla jednoczesnego użycia wątków. Oznacza to po prostu, że pasywna jest synchronizacja, która by na to pozwoliła, odkładając go na kod wywołujący, zamiast dostarczać go jako część jego wewnętrznej implementacji. Kompatybilny z wątkami to sposób, w jaki kod powinien być domyślnie zapisywany w większości przypadków, ale niestety jest to często błędnie uważane za niebezpieczne w wątku, tak jakby z natury było anty-bezpieczne, co jest głównym problemem dla programistów.
UWAGA: Wiele podręczników oprogramowania faktycznie używa terminu „bezpieczny dla wątków” w odniesieniu do „kompatybilnych z wątkami”, co jeszcze bardziej dezorientuje to, co już było bałaganem! Z tego właśnie powodu unikam terminów „bezpieczny dla wątków” i „bezpieczny dla wątków”, ponieważ niektóre źródła nazywają coś „bezpiecznym dla wątków”, podczas gdy inne nazywają to „niebezpiecznym dla wątków”, ponieważ nie mogą się zgodzić na temat tego, czy musisz spełnić dodatkowe standardy bezpieczeństwa (operacje podstawowe synchronizacji), czy po prostu NIE być wrogo nastawionym, aby zostać uznanym za „bezpieczny”. Unikaj więc tych warunków i używaj inteligentniejszych terminów, aby uniknąć niebezpiecznej nieporozumienia z innymi inżynierami.
Przypomnienie o naszych celach
Zasadniczo naszym celem jest obalenie chaosu.
Robimy to, tworząc deterministyczne systemy, na których możemy polegać. Determinizm jest drogi, głównie ze względu na koszty alternatywne utraty równoległości, potoków i ponownego zamawiania. Staramy się minimalizować determinizm, aby utrzymać nasze koszty na niskim poziomie, unikając przy tym podejmowania decyzji, które jeszcze bardziej osłabią ten niewielki determinizm, na jaki możemy sobie pozwolić.
Synchronizacja wątków polega na zwiększeniu porządku i zmniejszeniu chaosu. Poziomy, na których to robisz, odpowiadają wyżej wymienionym warunkom. Najwyższy poziom oznacza, że system zachowuje się w sposób całkowicie przewidywalny za każdym razem. Drugi poziom oznacza, że system zachowuje się na tyle dobrze, że wywołanie kodu może niezawodnie wykryć nieprzewidywalność. Na przykład fałszywy pobudka w zmiennej warunkowej lub błąd zablokowania muteksu, ponieważ nie jest on gotowy. Trzeci poziom oznacza, że system nie zachowuje się wystarczająco dobrze, aby grać z kimkolwiek innym i może być NIGDY uruchamiany tylko w jednym wątku bez wywoływania chaosu.
źródło
Zamiast myśleć o kodzie lub klasach jako bezpiecznych dla wątków, czy nie, myślę, że bardziej pomocne jest myślenie o działaniach jako bezpiecznych dla wątków. Dwie akcje są bezpieczne dla wątków, jeśli będą zachowywać się tak, jak określono podczas uruchamiania z dowolnych kontekstów wątków. W wielu przypadkach klasy będą obsługiwały niektóre kombinacje działań w sposób bezpieczny dla wątków, a inne nie.
Na przykład wiele kolekcji, takich jak listy tablic i zestawy skrótów, zagwarantuje, że jeśli początkowo są one dostępne wyłącznie za pomocą jednego wątku i nigdy nie zostaną zmodyfikowane po tym, jak odniesienie stanie się widoczne dla innych wątków, można je odczytać dowolnie za pomocą dowolnej kombinacji wątków bez ingerencji.
Co ciekawsze, niektóre zbiory zestawów skrótów, takie jak oryginalne nie-ogólne zbiory w .NET, mogą oferować gwarancję, że dopóki żaden element nie zostanie nigdy usunięty, i pod warunkiem, że zapisuje do nich tylko jeden wątek, każdy wątek, który próbuje czytaj kolekcja będzie zachowywać się tak, jakby uzyskiwała dostęp do kolekcji, w której aktualizacje mogą być opóźnione i występować w dowolnej kolejności, ale w przeciwnym razie będą zachowywać się normalnie. Jeśli wątek nr 1 dodaje X, a następnie Y, a wątek nr 2 szuka i widzi Y, a następnie X, byłoby możliwe, aby wątek nr 2 zobaczył, że Y istnieje, ale X nie; to, czy takie zachowanie jest „bezpieczne dla wątków”, zależy od tego, czy wątek nr 2 jest przygotowany na poradzenie sobie z tą możliwością.
Na koniec, niektóre klasy - zwłaszcza blokujące biblioteki komunikacyjne - mogą mieć metodę „close” lub „Dispose”, która jest bezpieczna dla wątków w odniesieniu do wszystkich innych metod, ale nie ma innych metod, które są bezpieczne dla wątków w odniesieniu do wzajemnie. Jeśli wątek wykona blokujące żądanie odczytu, a użytkownik programu kliknie „anuluj”, nie będzie możliwości wysłania żądania zamknięcia przez wątek, który próbuje wykonać odczyt. Żądanie zamknięcia / usunięcia może jednak asynchronicznie ustawić flagę, co spowoduje, że żądanie odczytu zostanie anulowane jak najszybciej. Po zamknięciu dowolnego wątku obiekt stałby się bezużyteczny, a wszystkie próby przyszłych działań zakończyłyby się niepowodzeniem natychmiast,
źródło
W najprostszych słowach: P Jeśli bezpieczne jest wykonywanie wielu wątków w bloku kodu, jest ono bezpieczne dla wątków *
* obowiązują warunki
Warunki są wymienione przez inne odpowiedzi, takie jak 1. Wynik powinien być taki sam, jeśli wykonasz jeden wątek lub wiele wątków nad nim itp.
źródło