Jason. Cała informatyka w jednym miejscu!

Zainteresowani jesteście programowaniem współbieżnym w języku Python :)? Jakbyście nie zostali do tej pory poinformowani, język ten oprócz oferowania pisania w kilku paradygmatach, wspiera także wielowątkowość. To Wam zapewni między innymi moduł "threading" w Pythonie jakim się teraz zajmiemy! I wbrew pozorom, programowanie współbieżności wcale nie wygląda na takie trudne w Pythonie :D.

MODUŁ "THREADING" DROGĄ DO WSPÓŁBIEŻNOŚCI

Zanim przejdziemy do kodu, pozwolę sobie zostawić parę słów na temat współbieżności w teorii.

WSPÓŁBIEŻNOŚĆ OD TEORETYCZNEJ STRONY

Programowanie wielowątkowe służy do wykonywania poszczególnych fragmentów programu jako osobnych instrukcji dotyczących tej samej aplikacji. Innymi słowy program sobie działa w tle tak jak zwykle, a w tym samym czasie (od momentu uruchomienia wątku) wykonuje się osobna część programu idąca swoim własnym torem i zajmuje się osobnym zbiorem instrukcji. Można powiedzieć, że głównym "aktorem" staje się tzw. "wątek". To jest fragment programu wykonujący podane przez programistę czynności w tym samym procesie.

Przykładów zastosowań można wymieniać od groma. Jednym z nich może być kontrolowanie czasu, który upłynął od momentu wysłania żądania pobrania pliku przeglądając w tym czasie pliki przez program. Jeżeli to trwa powiedzmy, 5 sekund, wtedy może się pojawić tekst w stylu "To może trochę potrwać" i monitorować postęp w procentach. Innym razem można zrobić sprawdzanie łącza internetowego czy podczas pobierania pliku nie doszło do przerwania sygnału i nie trwa ono zbyt długo (pobieranie "stoi w miejscu"). Wtedy można zatrzymać pobieranie i utworzyć komunikat "Błąd pobierania. Sprawdź swoje połączenie z Internetem". Znowu konieczne jest utworzenie wątku.

Generalnie wątki przydają się w każdej sytuacji, w której potrzebne (czy też wskazane) jest działanie przynajmniej dwóch rzeczy w tle. Wtedy jedno nie przeszkadza drugiemu, a procesor wielordzeniowy staje się o wiele bardziej przydatny. Oczywiście, nie jest przy nich tak kolorowo, że "świat jest pozbawiony wad" ;). Jednak nie będę tego tutaj roztrząsać, bardziej szczegółową teorię możecie przeczytać tutaj.

UTWORZENIE WĄTKU W PYTHONIE

Zgodnie z umieszczeniem informacji na samym początku, moduł "threading" w Pythonie jest jedną z dróg do tworzenia programów wielowątkowych (współbieżnych). Po wykonaniu poniższego importu modułu:

import threading

uzyskujemy pełnoprawny dostęp do klasy "Thread", dzięki której tworzymy nowe instancje wątków. Tworzymy ją tak:

t = threading.Thread(target = [funkcja])

Aby był sens tworzenia wątku, trzeba mu przydzielić jakieś instrukcje do wykonania. Właśnie w tym celu podajemy nazwany parametr funkcji, "target". Po znaku przypisania, podajemy bezbłędnie nazwę funkcji. Tylko uwaga! Tym razem bez ŻADNYCH nawiasów ani parametrów. Ani jednego! Sama nazwa, ponieważ "threading" w Pythonie oczekuje samej funkcji, a nie wywołania (wywoływać będzie później). Oto przykład prawidłowego zapisu:

def do_something():
    # dowolne instrukcje

t = threading.Thread(target=do_something)

Ostatnia rzecz to uruchomienie wątku. Nie odpala się on samoistnie zaraz po utworzeniu instancji, jak mogliście pomyśleć. Metoda "start" i wtedy wątek wkracza do akcji!

[zmienna wątkowa].start()

Na chwilę obecną, po uruchomieniu, nie zobaczycie niczego zaskakującego, gdyż włączony został tylko jeden wątek. Oczekiwane wyniki tracą swą przewidywalność gdy wątki się ze sobą ścigają, co prezentuję niżej! Zobaczcie co jeszcze w sobie kryje moduł "threading" w Pythonie!

WYŚCIG WĄTKÓW

Podkoloryzujmy nasz przykład. Załóżmy, że teraz do akcji dołączy drugi wątek wykonujący tę samą funkcję:

ta = threading.Thread(target=print_ten_numbers)
tb = threading.Thread(target=print_ten_numbers)

a funkcja ta wypisze nam liczby od 1 do 10:

def print_ten_numbers():
    for n in range(1, 11):
        print_(str(n))    # <-- celowa literówka w nazwie z powodu problemów z zapisem artykułu

Dorzućcie sobie kolejną instancję i teraz uruchomcie program. Jaki efekt?

Na wyjściu mamy "poszarpane" wręcz wypisania naszych liczb układające się w pewien wzór. DWA odstępy zamiast jednego oraz liczby postawione obok siebie. Dzieje się tak dlatego, iż oba wątki (czyli dwa osobne przebiegi programu) działają w tym samym czasie niezależnie od siebie. Kod nie jest już egzekwowany "liniowo" jedna instrukcja po drugiej, tylko instrukcje w podanej funkcji są wykonywane równolegle. Ponieważ wypisanie dziesięciu liczb nie jest wysokim obciążeniem dla procesora, liczby wypisane na wyjściu układają się wszędzie tak samo, natomiast może powstać inny wynik zależnie od procesora!

Działanie obu wątków równolegle z modułu "threading" w Pythonie

Wątki się ze sobą "ścigają" o pierwsze wykonanie swoich instrukcji przez co na wyjściu będą sobie "wchodzić w drogę".

FUNKCJA WĄTKU Z PARAMETRAMI

Wiem z autopsji, że ktokolwiek z Was BĘDZIE chciał się dowiedzieć czy jest jakaś metoda na przekazanie parametrów do funkcji którą zajmie się wątek. Moduł "threading" w Pythonie umożliwia jeden sposób ;). Dajmy na to, że pragniemy przypisać taką oto funkcję posiadającą jeden parametr, bez którego funkcja nie będzie w ogóle działać:

def print_n_numbers(times):
    print_("Wypisuję " + str(times) + " liczb(y)")    # <-- celowa literówka w nazwie z powodu problemów z zapisem artykułu

    for n in range(1, times + 1):
        print_(str(n))    # <-- celowa literówka w nazwie z powodu problemów z zapisem artykułu

W takim układzie, sam "target" tutaj nie wystarczy. Zatrzyma Was zgłoszony wyjątek na dzień dobry :). Aby przekazywanie parametrów aktualnych stało się faktem, dokonuje się niekonwencjonalnego obejścia w postaci dorzucenia następnego parametru o nazwie "args" (kolejne nawiązanie do języka C). Po znaku równości (przypisania), podajemy w nawiasach okrągłych parametry jakie mają "wejść" w to samo miejsce, co zwykle.

Jest jedno "ale"! Gdy skończycie przekazywać parametry, zostawiacie przecinek na samym końcu, zaraz po nawiasie zamykającym! Czyli w naszym przykładzie, moduł "threading" w Pythonie wymaga takiego zapisu:

ta = threading.Thread(target=print_n_numbers, args=(30,))

Jeżeli posłusznie zostawicie na samym końcu przecinek, program bez żadnych "czepianek" pozwoli wątkowi działać z funkcją razem z otrzymanym parametrem. Zrobi swoje i cześć ;).

Przekazywanie wątkowi z modułu "threading" w Pythonie parametru do funkcji

Dzięki nietypowemu zapisowi w parametrze "args", dostępna jest możliwość przekazywania parametrów aktualnych do funkcji jaką ma się zająć wątek.

ZWROT WARTOŚCI FUNKCJI Z WĄTKU

Moduł "threading" w Pythonie nie udostępnia takiej możliwości, ale przy użyciu bliźniaczej formy programowania współbieżnego, pakietu "concurrent", staje się to już możliwe.


To wszystko co chciałem zaprezentować. Python pozwala naprawdę wręcz "żonglować" metodami kodowania różnych funkcjonalności, które w innych językach wysokiego poziomu nikomu by nie przyszły do głowy :).

PODOBNE ARTYKUŁY