Wielowątkowość w Pythonie [z przykładami kodowania]

Opublikowany: 2020-11-30

Ulepszanie i przyspieszanie kodu to kolejny krok po zdobyciu podstawowej wiedzy o Pythonie. Wielowątkowość jest jednym ze sposobów osiągnięcia tej optymalizacji za pomocą „wątków”. Czym są te wątki? A czym różnią się one od procesów? Dowiedzmy Się.

Pod koniec tego samouczka zdobędziesz wiedzę w zakresie:

  • Czym są wątki i procesy?
  • Jak osiąga się wielowątkowość?
  • Jakie są jego ograniczenia?
  • Kiedy używać wielowątkowości?

Ucz się kursów nauki danych online z najlepszych uniwersytetów na świecie. Zdobywaj programy Executive PG, Advanced Certificate Programs lub Masters Programs, aby przyspieszyć swoją karierę.

Spis treści

Wątki w Pythonie

Kiedy myślimy o wielozadaniowości, myślimy o równoległym wykonywaniu. Wielowątkowość nie jest ściśle równoległa. Wątki mogą być traktowane jako oddzielne jednostki przepływu wykonywania różnych części programu, które działają niezależnie. Zasadniczo wątki nie działają równolegle, ale Python przełącza się z jednego wątku do drugiego tak szybko, że wydaje się, że są równoległe.

Z drugiej strony procesy są ściśle równoległe i działają na różnych rdzeniach, aby osiągnąć szybsze wykonanie. Wątki mogą być również uruchamiane na różnych procesorach, ale technicznie nadal nie będą działać równolegle.

A czy myślisz, że jeśli wątki nie biegną równolegle, to jak przyspieszają działanie? Odpowiedź brzmi, że nie zawsze przyspieszają przetwarzanie. Wielowątkowość jest szczególnie używana w zadaniach, w których wątki przyspieszają przetwarzanie.

Wszystkie informacje o wątku są zawarte w bloku kontroli wątków (TCB) . TCB składa się z następujących głównych części:

  1. Unikalny identyfikator TID – identyfikator gwintu
  2. Wskaźnik stosu, który wskazuje na stos wątku w procesie
  3. Licznik programu, który przechowuje adres instrukcji aktualnie wykonywanej przez wątek
  4. Stan wątku (działający, gotowy, oczekujący, uruchomiony lub gotowy)

Powiedziawszy to, procesy mogą zawierać wiele wątków, które współdzielą kod, dane i wszystkie pliki. Wszystkie wątki mają swój własny rejestr i stos, do których mają dostęp.

Teraz możesz się zastanawiać, czy wątki używają wspólnych danych i kodu, jak mogą z nich korzystać bez utrudniania innych wątków. Jest to największe ograniczenie wielowątkowości, o którym powiemy w dalszej części tego samouczka.

Przełączanie kontekstu

Teraz, jak opisano powyżej, wątki nie działają równolegle, ale w konsekwencji. Tak więc, gdy jeden wątek T1 rozpocznie wykonywanie, wszystkie inne wątki pozostają w trybie oczekiwania. Dopiero po zakończeniu T1 z jego wykonaniem, dowolny inny wątek w kolejce może rozpocząć wykonywanie. Python przełącza się z jednego wątku do drugiego tak szybko, że wydaje się to być wykonywaniem równoległym. To przełączanie nazywamy „przełączaniem kontekstu”.

Programowanie wielowątkowe

Rozważ poniższy kod, który używa wątków do wykonania operacji na kostce i na kwadracie.

importuj wątki

def kostka (n) :
print( „Kostka: {}” .format(n * n * n))

def kwadrat (n) :
print( “Kwadrat: {}” .format(n * n))

if __name__ == „__main__” :
# utwórz wątek
t1 = wątki.Wątek(target=kwadrat, args=( 5 ,))
t2 = Threading.Thread(target=cuber, args=( 5 ,))

# rozpocznij wątek t1
t1.start()
# rozpocznij wątek t2
t2.start()

# poczekaj, aż t1 się zakończy
t1.dołącz()
# poczekaj, aż t2 się zakończy
t2.dołącz()

# oba wątki zakończone
print( „Gotowe!” )

#Wyjście:
Kwadrat: 25
Kostka: 125
Gotowy!

Teraz spróbujmy zrozumieć kod.

Najpierw importujemy moduł Threading, który odpowiada za wszystkie zadania. Wewnątrz main tworzymy 2 wątki tworząc podklasy klasy Thread. Musimy przekazać cel, czyli funkcję, która ma zostać wykonana w tym wątku, oraz argumenty, które należy przekazać do tych funkcji.

Teraz po zadeklarowaniu wątków musimy je uruchomić. Odbywa się to poprzez wywołanie metody start na wątkach. Po uruchomieniu program główny musi poczekać na zakończenie przetwarzania wątków. Używamy metody wait , aby pozwolić głównemu programowi zatrzymać się i poczekać, aż wątki T1 i T2 zakończą swoje działanie.

Musisz przeczytać: Wyzwania Pythona dla początkujących

Synchronizacja wątków

Jak omówiliśmy powyżej, wątki nie działają równolegle, zamiast tego Python przełącza się między sobą. Tak więc istnieje bardzo krytyczna potrzeba prawidłowej synchronizacji między wątkami, aby uniknąć dziwnych zachowań.

Warunki wyścigu

Wątki podlegające temu samemu procesowi wykorzystują wspólne dane i pliki, co może prowadzić do „wyścigu” o dane między wieloma wątkami. W związku z tym, jeśli dostęp do danych uzyskuje się przez wiele wątków, zostanie on zmodyfikowany przez oba wątki, a wyniki, które otrzymamy, nie będą zgodne z oczekiwaniami. Nazywa się to stanem wyścigu.

Tak więc, jeśli masz dwa wątki, które mają dostęp do tych samych danych, oba mogą uzyskać do nich dostęp i je zmodyfikować, gdy ten konkretny wątek jest wykonywany. Więc kiedy T1 zaczyna wykonywać i modyfikuje niektóre dane, T2 jest w trybie uśpienia/czekania. Następnie T1 zatrzymuje wykonanie i przechodzi w stan uśpienia przekazując kontrolę T2, który również ma dostęp do tych samych danych. Tak więc T2 zmodyfikuje teraz i nadpisze te same dane, które doprowadzą do problemów, gdy T1 zacznie się ponownie.

Celem synchronizacji wątków jest upewnienie się, że ten stan wyścigu nigdy nie nadejdzie, a krytyczna sekcja kodu jest dostępna dla wątków pojedynczo w zsynchronizowany sposób.

Zamki

Aby rozwiązać i zapobiec sytuacji wyścigu i jego konsekwencjom, moduł wątków oferuje klasę Lock , która używa Semaforów do pomocy w synchronizacji wątków. Semafory to nic innego jak flagi binarne. Potraktuj je jako znak „Zaangażowany” na budkach telefonicznych, które mają wartość „Zaangażowany” (odpowiednik 1) lub „Nie zaangażowany” (odpowiednik 0). Więc za każdym razem, gdy wątek natrafi na segment kodu z blokadą, musi sprawdzić, czy blokada jest już w 1 stanie. Jeśli tak, będzie musiał poczekać, aż stanie się 0, aby mógł z niego skorzystać.

Klasa Lock ma dwie podstawowe metody:

  1. akwizycja([blokowanie]) : Metoda akwizycji przyjmuje parametry blokowania jako True lub False . Jeśli blokada dla wątku T1 została zainicjowana z blokowaniem jako True, będzie czekać lub pozostanie zablokowana, aż krytyczna część kodu zostanie zablokowana przez inny wątek T2. Gdy drugi wątek T2 zwolni blokadę, wątek T1 przejmuje blokadę i zwraca True .

Z drugiej strony, jeśli blokada wątku T1 została zainicjowana z blokowaniem parametru jako False , wątek T1 nie będzie czekał lub pozostanie zablokowany, jeśli sekcja krytyczna jest już zablokowana przez wątek T2. Jeśli uzna go za zablokowany, od razu zwróci False i wyjdzie. Jeśli jednak kod nie został zablokowany przez inny wątek, uzyska blokadę i zwróci True .

release() : Kiedy metoda release zostanie wywołana na zamku, odblokuje blokadę i zwróci True. Sprawdzi również, czy jakieś wątki czekają na zwolnienie blokady. Jeśli tak, to pozwoli dokładnie jednemu z nich uzyskać dostęp do zamka.

Jeśli jednak blokada jest już odblokowana, zgłaszany jest błąd ThreadError.

Zakleszczenia

Kolejną kwestią, która pojawia się, gdy mamy do czynienia z wieloma blokadami, jest – Deadlocks. Zakleszczenia występują, gdy blokady nie są zwalniane przez wątki z różnych powodów. Rozważmy prosty przykład, w którym wykonujemy następujące czynności:

importuj wątki

l = wątkowanie.Lock()
# Przed pierwszym nabyciem
l.uzyskaj()
# Przed 2. nabyciem
l.uzyskaj()
# Teraz zdobyłeś zamek dwa razy

W powyższym kodzie dwukrotnie wywołujemy metodę nabywania, ale nie zwalniamy jej po pierwszym nabyciu. W związku z tym, gdy Python zobaczy drugą instrukcję nabywania, przejdzie w tryb oczekiwania na czas nieokreślony, ponieważ nigdy nie zwolniliśmy poprzedniej blokady.

Te warunki impasu mogą wkraść się do twojego kodu, nawet nie zdając sobie z tego sprawy. Nawet jeśli dołączysz wywołanie zwolnienia, Twój kod może się nie powieść w połowie drogi, a zwolnienie nigdy nie zostanie wywołane, a blokada pozostanie zablokowana. Jednym ze sposobów na pokonanie tego jest użycie instrukcji with as , zwanej także menedżerami kontekstu. Używając instrukcji with as , blokada zostanie automatycznie zwolniona po zakończeniu przetwarzania lub niepowodzeniu z jakiegokolwiek powodu.

Przeczytaj: Pomysły i tematy dotyczące projektów w Pythonie

Zanim pójdziesz

Jak wspomnieliśmy wcześniej, wielowątkowość nie jest przydatna we wszystkich aplikacjach, ponieważ tak naprawdę nie powoduje, że wszystko działa równolegle. Ale głównym zastosowaniem wielowątkowości są zadania I/O, w których procesor siedzi bezczynnie, czekając na załadowanie danych. Wielowątkowość odgrywa tutaj kluczową rolę, ponieważ ten czas bezczynności procesora jest wykorzystywany do innych zadań, dzięki czemu idealnie nadaje się do optymalizacji.

Jeśli jesteś zainteresowany nauką o danych, sprawdź program IIIT-B i upGrad Executive PG w dziedzinie Data Science , który jest stworzony dla pracujących profesjonalistów i oferuje ponad 10 studiów przypadków i projektów, praktyczne warsztaty praktyczne, mentoring z ekspertami z branży, 1 -on-1 z mentorami branżowymi, ponad 400 godzin nauki i pomocy w pracy z najlepszymi firmami.

Czym jest wątek w Pythonie?

Wątki to jednostki w procesie, które mogą być zaplanowane do wykonania w Pythonie. Mówiąc potocznie, wątek to proces obliczeniowy przeprowadzany przez komputer. Jest to zestaw takich instrukcji w programie, które programiści mogą uruchamiać niezależnie od innych skryptów. Wątki umożliwiają zwiększenie szybkości aplikacji przy użyciu równoległości. Jest to lekki proces, który umożliwi równoległe działanie zadań. Wątki działają niezależnie i maksymalizują wykorzystanie procesora, poprawiając w ten sposób wydajność procesora.

Jaki jest pożytek z wielowątkowości w Pythonie?

Wielowątkowość to technika wielowątkowości w programowaniu w Pythonie, która umożliwia jednoczesne działanie wielu wątków poprzez szybkie przełączanie między wątkami za pomocą procesora (tzw. przełączanie kontekstu). Kiedy możemy podzielić nasze zadanie na wiele oddzielnych sekcji, stosujemy wielowątkowość. Załóżmy na przykład, że musisz przeprowadzić złożone zapytanie do bazy danych, aby uzyskać dane i podzielić je na wiele pojedynczych zapytań. W takim przypadku lepiej będzie przydzielić wątek do każdego zapytania i uruchomić je wszystkie równolegle.

Co to jest synchronizacja wątków?

Synchronizacja wątków jest opisana jako metoda, która gwarantuje, że co najmniej dwa współbieżne procesy lub wątki nie wykonują jednocześnie kluczowego elementu programu. Metody synchronizacji służą do kontroli dostępu procesów do ważnych sekcji. Kiedy uruchamiamy dwa lub więcej wątków w programie, istnieje szansa, że ​​kilka wątków może próbować uzyskać dostęp do tego samego zasobu, co skutkuje nieoczekiwanymi wynikami z powodu problemów ze współbieżnością. Na przykład, jeśli wiele wątków próbuje pisać w tym samym pliku, dane mogą zostać uszkodzone, ponieważ jeden z wątków może zastąpić dane lub gdy jeden wątek otwiera się, a inny wątek zamyka ten sam plik.