Blog JSystems - z miłości do programowania

Szukaj

Uruchamianie operacji w osobnych wątkach

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 - implementacja w C

  • PyPy - implementacja w Pythonie

  • Jython - implementacja w Javie

  • IronPython - implementacja w .NET


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.


 


Pakiet threading


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.


 


Wątek jako demon


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.


 


Odbieranie wartości z wątku


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)

Przyjdź do nas na szkolenie z języka Python! Mamy szereg szkoleń w ofercie, od podstawowych po aplikacje webowe z użyciem Django, analizę danych, tesowanie, machine learning i wiele innych. Sprawdź dostępne szkolenia Python
Zapisz się do newslettera aby otrzymywać najnowsze świeżynki pojawiające się na blogu! Zapisz się do newslettera