Wątki umożliwiają wykonywane kilku czynności równolegle. Są to częsci jednego programu (procesu) które mogą działać jednocześnie i współdzielić zasoby.
Zanim zaczniemy omawiać implementację wielowątkowości w Pythonie, musimy nieco wyjaśnić kilka ważnych spraw.
Python jest językiem programowania ale posiada kilka implementacji. Są to m.in:
CPython i PyPy nie są przystosowane do wykonywania wielu czynności jednocześnie, dlatego też posiadają GIL (Global Interpreter Lock) . GIL powoduje że tylko jeden wątek ma dostęp do interpretera w danej chwili. Jest to rozwiązanie problemu z jednoczesnym dostępem do tych samych zasobów w tym samym czasie. Wszystkie wątki w związku z działaniem GILa będą szeregowane, czyli w rzeczywistości nie będą wykonywane równocześnie, a naprzemiennie. Dziać się to będzie jednak na tyle szybko, że będzie to wygladało jakby były wykonywane równolegle.
Przyjrzyjmy się poniższemu przykładowi:
import time
import threading
def zamul(nazwa):
print(f'start wątku {nazwa}\n')
time.sleep(3)
print(f'koniec wątku {nazwa}\n')
x=threading.Thread(target=zamul,args=('boczny',))
x.start()
zamul('glówny')
Funkcja "zamul" wypisuje informacje o rozpoczęciu wątku, wstrzymuje działanie na 3 sekundy i wypisuje informację o zakończeniu wątku. Korzystając z klasy Thread znajdującej się w pakiecie threading tworzę obiekt wątku. Tworząc go, jako argumenty przekazuję funkcję do wykonania oraz argumenty które zostaną przekazane do tej funkcji. Za pomocą "x.start()" rozpoczynam ten wątek, a chwilę później uruchamiam funkcję "zamul" bezpośrednio w głównym wątku programu. Wynik działania na konsoli:
start wątku boczny
start wątku glówny
koniec wątku glówny
koniec wątku boczny
Jeśli uruchomisz ten kod u siebie kolejność wyświetlonych komunikatów na konsoli może być różna przy każdym uruchomieniu. Wynika to z tak zwanego zjawiska wyścigów, które są bezpośrednim skutkiem niedeterministyczności chwili wywłaszczania (przerywania i przełączania) wątków.
Zauważ że dotychczas uruchamiane wątki wykonywały się do końca, wątek główny czekał na zakończenie wszystkich wątków. Możesz też uruchomić wątek w trybie demona. Spowodujesz w ten sposób że wszystkie poboczne wątki będą kończyły pracę z zakończeniem głównego wątku programu, a główny wątek programu nie będzie czekał na zakończenie wątków bocznych. Przetestuj poniższy kod:
import time
import threading
def odliczaj(nazwa_watku, ile):
for x in range(ile):
print(x)
time.sleep(1)
f=threading.Thread(target=odliczaj,args=('pierwszy',10),daemon=True)
f.start()
Program wypisuje na konsoli tylko 0, po czym kończy działanie. Gdybyś zechciał by główny wątek poczekał na zakończenie wątku demona, trzeba wywołać funkcję "join()" na obiekcie wątku:
import time
import threading
def odliczaj(nazwa_watku, ile):
for x in range(ile):
print(x)
time.sleep(1)
f=threading.Thread(target=odliczaj,args=('pierwszy',10),daemon=True)
f.start()
f.join()
Wywołanie join spowoduje że główny wątek zatrzyma się na tej linii i będzie czekał z dalszym wykonaniem programu do momentu zakończenia działania wątku dziecka.
Aby odebrać wartości zwracaną z funkcji uruchomionej w ramach "bocznego" wątku musimy posłużyć się inną wysokopoziomową biblioteką Pythona:
import time
import concurrent.futures
def oddaj():
time.sleep(3)
return 'koza'
wykonawca=concurrent.futures.ThreadPoolExecutor()
zadanie = wykonawca.submit(oddaj)
zwrot = zadanie.result()
print(zwrot)
Komentarze (0)
Brak komentarzy...