Wątkowanie w Pythonie: wprowadzenie
Opublikowany: 2022-10-25W tym samouczku dowiesz się, jak korzystać z wbudowanego modułu wątkowości Pythona do odkrywania możliwości wielowątkowości w Pythonie.
Zaczynając od podstaw procesów i wątków, dowiesz się, jak działa wielowątkowość w Pythonie — jednocześnie rozumiejąc koncepcje współbieżności i równoległości. Następnie dowiesz się, jak uruchomić i uruchomić jeden lub więcej wątków w Pythonie za pomocą wbudowanego modułu threading
.
Zacznijmy.
Procesy a wątki: jakie są różnice?
Czym jest proces?
Proces to dowolna instancja programu, który musi zostać uruchomiony.
Może to być cokolwiek – skrypt Pythona lub przeglądarka internetowa, taka jak Chrome, po aplikację do wideokonferencji. Jeśli uruchomisz Menedżera zadań na swoim komputerze i przejdziesz do Wydajność -> CPU , będziesz mógł zobaczyć procesy i wątki, które są aktualnie uruchomione na rdzeniach twojego procesora.

Zrozumienie procesów i wątków
Wewnętrznie proces ma dedykowaną pamięć, która przechowuje kod i dane odpowiadające procesowi.
Proces składa się z co najmniej jednego wątku . Wątek to najmniejsza sekwencja instrukcji, które system operacyjny może wykonać i reprezentuje przepływ wykonywania.
Każdy wątek ma swój własny stos i rejestry, ale nie ma dedykowanej pamięci. Wszystkie wątki powiązane z procesem mogą uzyskać dostęp do danych. Dlatego dane i pamięć są współdzielone przez wszystkie wątki procesu.

W procesorze z N rdzeniami N procesów może być wykonywanych równolegle w tym samym czasie. Jednak dwa wątki tego samego procesu nigdy nie mogą być wykonywane równolegle, ale mogą być wykonywane jednocześnie. W następnej sekcji zajmiemy się pojęciem współbieżności i równoległości.
Bazując na tym, czego się do tej pory dowiedzieliśmy, podsumujmy różnice między procesem a wątkiem.
Funkcja | Proces | Wątek |
Pamięć | Pamięć dedykowana | Pamięć współdzielona |
Tryb wykonania | Równoległy, współbieżny | Równoległy; ale nie równolegle |
Wykonanie obsługiwane przez | System operacyjny | Tłumacz języka CPython |
Wielowątkowość w Pythonie
W Pythonie Global Interpreter Lock (GIL) zapewnia, że tylko jeden wątek może uzyskać blokadę i działać w dowolnym momencie. Wszystkie wątki powinny uzyskać tę blokadę do uruchomienia. Gwarantuje to, że tylko jeden wątek może być wykonywany — w dowolnym momencie — i pozwala uniknąć jednoczesnej wielowątkowości.
Rozważmy na przykład dwa wątki, t1
i t2
, tego samego procesu. Ponieważ wątki współdzielą te same dane, gdy t1
odczytuje określoną wartość k
, t2
może modyfikować tę samą wartość k
. Może to prowadzić do zakleszczeń i niepożądanych wyników. Ale tylko jeden z wątków może uzyskać blokadę i działać w dowolnym wystąpieniu. Dlatego GIL zapewnia również bezpieczeństwo gwintów .
Jak więc osiągnąć wielowątkowość w Pythonie? Aby to zrozumieć, omówmy koncepcje współbieżności i równoległości.
Współbieżność a równoległość: przegląd
Rozważ procesor z więcej niż jednym rdzeniem. Na poniższej ilustracji procesor ma cztery rdzenie. Oznacza to, że w dowolnym momencie możemy wykonywać równolegle cztery różne operacje.
Jeśli są cztery procesy, to każdy z nich może działać niezależnie i jednocześnie na każdym z czterech rdzeni. Załóżmy, że każdy proces ma dwa wątki.

Aby zrozumieć, jak działa wątkowość, przejdźmy z architektury wielordzeniowej do jednordzeniowej. Jak wspomniano, tylko jeden wątek może być aktywny w określonej instancji wykonania; ale rdzeń procesora może przełączać się między wątkami.

Na przykład wątki związane z we/wy często czekają na operacje we/wy: odczytywanie danych wejściowych użytkownika, odczyty bazy danych i operacje na plikach. Podczas tego czasu oczekiwania może zwolnić blokadę, aby drugi wątek mógł działać. Czas oczekiwania może być również prostą operacją, taką jak spanie przez n
sekund.
Podsumowując: Podczas operacji oczekiwania wątek zwalnia blokadę, umożliwiając przełączenie rdzenia procesora na inny wątek. Wcześniejszy wątek wznawia wykonywanie po zakończeniu okresu oczekiwania. Ten proces, w którym rdzeń procesora jednocześnie przełącza się między wątkami, ułatwia wielowątkowość.
Jeśli chcesz zaimplementować równoległość na poziomie procesu w swojej aplikacji, rozważ zamiast tego użycie wieloprocesorowości.
Moduł wątkowości w Pythonie: pierwsze kroki
Python jest dostarczany z modułem threading
, który można zaimportować do skryptu Pythona.
import threading
Aby utworzyć obiekt wątku w Pythonie, możesz użyć konstruktora Thread
: threading.Thread(...)
. Jest to ogólna składnia, która wystarcza w przypadku większości implementacji wątków:
threading.Thread(target=...,args=...)
Tutaj,
-
target
jest argumentem słowa kluczowego oznaczającym wywoływalne w Pythonie -
args
jest krotką argumentów, które przyjmuje cel.
Będziesz potrzebować Pythona 3.x do uruchomienia przykładów kodu w tym samouczku. Pobierz kod i postępuj zgodnie z instrukcjami.
Jak definiować i uruchamiać wątki w Pythonie
Zdefiniujmy wątek, który uruchamia funkcję docelową.
Funkcja docelowa to some_func
.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() print(threading.active_count())
Przeanalizujmy, co robi powyższy fragment kodu:

- Importuje moduły
threading
itime
. - Funkcja
some_func
ma opisowe instrukcjeprint()
i zawiera operację uśpienia na dwie sekundy:time.sleep(n)
powoduje uśpienie funkcji nan
sekund. - Następnie definiujemy wątek
thread_1
z celem jakosome_func
.threading.Thread(target=...)
tworzy obiekt wątku. - Uwaga : Podaj nazwę funkcji, a nie wywołanie funkcji; użyj
some_func
a niesome_func()
. - Tworzenie obiektu wątku nie rozpoczyna wątku; wywołuje metodę
start()
na obiekcie wątku. - Aby uzyskać liczbę aktywnych wątków, używamy funkcji
active_count()
.
Skrypt Pythona działa w głównym wątku i tworzymy kolejny wątek ( thread1
), aby uruchomić funkcję some_func
, więc liczba aktywnych wątków wynosi dwa, jak widać na wyjściu:
# Output Running some_func... 2 Finished running some_func.
Jeśli przyjrzymy się bliżej wynikowi, zobaczymy, że po uruchomieniu thread1
, uruchamiana jest pierwsza instrukcja print . Jednak podczas operacji uśpienia procesor przełącza się na wątek główny i drukuje liczbę aktywnych wątków — bez czekania na zakończenie wykonywania thread1
.

Oczekiwanie na zakończenie wykonywania wątków
Jeśli chcesz, aby thread1
zakończył wykonywanie, możesz wywołać na nim metodę join()
po uruchomieniu wątku. Spowoduje to oczekiwanie na zakończenie wykonywania thread1
bez przełączania się do głównego wątku.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() thread1.join() print(threading.active_count())
Teraz thread1
zakończył wykonywanie, zanim wydrukujemy liczbę aktywnych wątków. Tak więc działa tylko główny wątek, co oznacza, że liczba aktywnych wątków wynosi jeden.
# Output Running some_func... Finished running some_func. 1
Jak uruchomić wiele wątków w Pythonie
Następnie utwórzmy dwa wątki, aby uruchomić dwie różne funkcje.
Tutaj count_down
jest funkcją, która przyjmuje liczbę jako argument i odlicza od tej liczby do zera.
def count_down(n): for i in range(n,-1,-1): print(i)
Definiujemy count_up
, kolejną funkcję Pythona, która liczy od zera do podanej liczby.
def count_up(n): for i in range(n+1): print(i)
W przypadku używania funkcji
range()
ze składniąrange(start, stop, step)
, domyślnie wykluczony jest punktstop
.– Aby odliczać od określonej liczby do zera, można użyć ujemnej wartości
step
równej -1 i ustawić wartośćstop
na -1, aby uwzględnić zero.– Podobnie, aby liczyć do
n
, musisz ustawić wartośćstop
nan + 1
. Ponieważ domyślne wartościstart
istep
to odpowiednio 0 i 1, możesz użyćrange(n + 1)
, aby uzyskać sekwencję od 0 do n.
Następnie definiujemy dwa wątki, thread1
i thread2
, aby uruchomić odpowiednio funkcje count_down
i count_up
. Do obu funkcji dodajemy instrukcje print
i operacje sleep
.
Podczas tworzenia obiektów wątków zwróć uwagę, że argumenty funkcji docelowej powinny być określone jako krotka — do parametru args
. Ponieważ obie funkcje ( count_down
i count_up
) przyjmują jeden argument. Będziesz musiał wyraźnie wstawić przecinek po wartości. Zapewnia to, że argument jest nadal przekazywany jako krotka, ponieważ kolejne elementy są wywnioskowane jako None
.
import threading import time def count_down(n): for i in range(n,-1,-1): print("Running thread1....") print(i) time.sleep(1) def count_up(n): for i in range(n+1): print("Running thread2...") print(i) time.sleep(1) thread1 = threading.Thread(target=count_down,args=(10,)) thread2 = threading.Thread(target=count_up,args=(5,)) thread1.start() thread2.start()
Na wyjściu:
- Funkcja
count_up
działa nathread2
i liczy do 5, zaczynając od 0. - Funkcja
count_down
działa nathread1
odlicza od 10 do 0.
# Output Running thread1.... 10 Running thread2... 0 Running thread1.... 9 Running thread2... 1 Running thread1.... 8 Running thread2... 2 Running thread1.... 7 Running thread2... 3 Running thread1.... 6 Running thread2... 4 Running thread1.... 5 Running thread2... 5 Running thread1.... 4 Running thread1.... 3 Running thread1.... 2 Running thread1.... 1 Running thread1.... 0
Możesz zobaczyć, że thread1
i thread2
wykonują się naprzemiennie, ponieważ oba wymagają operacji oczekiwania (uśpienia). Gdy funkcja count_up
zakończy zliczanie do 5, thread2
nie jest już aktywny. Tak więc otrzymujemy dane wyjściowe odpowiadające tylko thread1
.
Podsumowując
W tym samouczku nauczyłeś się, jak korzystać z wbudowanego modułu wątków Pythona do implementacji wielowątkowości. Oto podsumowanie najważniejszych wniosków:
- Konstruktor Thread może służyć do tworzenia obiektu wątku. Użycie threading.Thread(target=<callable>,args=(<krotka argumentów>)) tworzy wątek, który uruchamia obiekt docelowy z argumentami określonymi w args .
- Program w języku Python działa w głównym wątku, więc tworzone obiekty wątku są dodatkowymi wątkami. Możesz wywołać funkcję active_count() zwracającą liczbę aktywnych wątków w dowolnej instancji.
- Możesz uruchomić wątek za pomocą metody start() na obiekcie wątku i poczekać, aż zakończy się wykonywanie za pomocą metody join() .
Możesz zakodować dodatkowe przykłady, dostosowując czasy oczekiwania, próbując wykonać inną operację we/wy i nie tylko. Pamiętaj, aby zaimplementować wielowątkowość w nadchodzących projektach Pythona. Udanego kodowania!