W szczególności w Pythonie, w jaki sposób zmienne są udostępniane między wątkami?
Chociaż korzystałem threading.Thread
wcześniej, nigdy tak naprawdę nie rozumiałem ani nie widziałem przykładów udostępniania zmiennych. Czy są one wspólne dla głównego wątku i dzieci, czy tylko wśród dzieci? Kiedy muszę używać lokalnego magazynu wątków, aby uniknąć tego udostępniania?
Widziałem wiele ostrzeżeń dotyczących synchronizowania dostępu do współdzielonych danych między wątkami przy użyciu blokad, ale nie widziałem jeszcze naprawdę dobrego przykładu problemu.
Z góry dziękuję!
python
multithreading
thread-local
Mikrofon
źródło
źródło
Odpowiedzi:
W Pythonie wszystko jest współdzielone, z wyjątkiem zmiennych lokalnych funkcji (ponieważ każde wywołanie funkcji otrzymuje własny zestaw zmiennych lokalnych, a wątki są zawsze oddzielnymi wywołaniami funkcji). A nawet wtedy tylko same zmienne (nazwy, które odnoszą się do obiektów) są lokalne dla funkcji; same obiekty są zawsze globalne i wszystko może się do nich odnosić.
Thread
Obiektu dla konkretnego wątku nie jest szczególny przedmiot w tym względzie. Jeśli przechowujeszThread
obiekt w miejscu, do którego mają dostęp wszystkie wątki (jak zmienna globalna), wszystkie wątki będą miały dostęp do tego jednegoThread
obiektu. Jeśli chcesz atomowo zmodyfikować wszystko , do czego inny wątek ma dostęp, musisz zabezpieczyć go blokadą. Wszystkie wątki muszą oczywiście mieć tę samą blokadę, inaczej nie byłoby to zbyt skuteczne.Jeśli chcesz mieć rzeczywistą pamięć lokalną dla wątków, właśnie tam
threading.local
jest. Atrybutythreading.local
nie są współdzielone między wątkami; każdy wątek widzi tylko atrybuty, które sam tam umieścił. Jeśli jesteś ciekawy jego implementacji, źródło znajduje się w _threading_local.py w bibliotece standardowej.źródło
Rozważ następujący kod:
Tutaj Threading.local () jest używany jako szybki i brudny sposób na przekazanie niektórych danych z run () do bar () bez zmiany interfejsu foo ().
Pamiętaj, że użycie zmiennych globalnych nie załatwi sprawy:
W międzyczasie, gdybyś mógł pozwolić sobie na przekazywanie tych danych jako argumentu funkcji foo () - byłby to bardziej elegancki i dobrze zaprojektowany sposób:
Ale nie zawsze jest to możliwe, gdy używasz kodu innej firmy lub źle zaprojektowanego kodu.
źródło
Możesz utworzyć lokalny magazyn wątków za pomocą
threading.local()
.Dane przechowywane w tls będą unikalne dla każdego wątku, co pomoże zapewnić, że nie nastąpi niezamierzone udostępnianie.
źródło
Podobnie jak w każdym innym języku, każdy wątek w Pythonie ma dostęp do tych samych zmiennych. Nie ma rozróżnienia między wątkiem głównym a wątkami podrzędnymi.
Jedyną różnicą w stosunku do Pythona jest to, że globalna blokada interpretera oznacza, że tylko jeden wątek może jednocześnie uruchamiać kod Pythona. Nie jest to jednak zbyt pomocne, jeśli chodzi o synchronizację dostępu, ponieważ wszystkie typowe problemy z wywłaszczaniem nadal mają zastosowanie i musisz używać prymitywów wątków, tak jak w innych językach. Oznacza to jednak, że musisz ponownie rozważyć, czy używasz wątków do wydajności.
źródło
Mogę się tutaj mylić. Jeśli wiesz inaczej, wyjaśnij to, ponieważ pomogłoby to wyjaśnić, dlaczego należałoby użyć wątku local ().
Wydaje się, że to stwierdzenie nie jest błędne: „Jeśli chcesz atomowo zmodyfikować wszystko, do czego inny wątek ma dostęp, musisz zabezpieczyć to blokadą”. Myślę, że to stwierdzenie jest -> skutecznie <- słuszne, ale nie do końca trafne. Pomyślałem, że termin „atomowy” oznacza, że interpreter Pythona utworzył fragment kodu bajtowego, który nie zostawiał miejsca na sygnał przerwania przesyłany do procesora.
Myślałem, że operacje atomowe to fragmenty kodu bajtowego Pythona, które nie dają dostępu do przerwań. Instrukcje Pythona, takie jak „running = True”, są niepodzielne. W tym przypadku nie musisz blokować procesora przed przerwaniami (uważam). Podział kodu bajtowego Pythona jest zabezpieczony przed przerwaniem wątku.
Kod Pythona, taki jak „thread_running [5] = True”, nie jest atomowy. Są tutaj dwa fragmenty kodu bajtowego Pythona; jeden, aby usunąć odniesienie do list () dla obiektu i inny fragment kodu bajtowego, aby przypisać wartość do obiektu, w tym przypadku „miejsce” na liście. Przerwanie można zgłosić -> pomiędzy <- dwoma kodami bajtowymi -> fragmentami <-. To było złe rzeczy.
Jak wątek local () ma się do „atomic”? Dlatego to stwierdzenie wydaje mi się mylące. Jeśli nie, czy możesz wyjaśnić?
źródło