Knjiga - mrežno i paralelno programiranje
2. Višedretveno programiranje
Istovremeno odvijanje zadaća unutar jednog programa – višedretveno programiranje
U prethodnim smo primjerima imali jedan poslužitelj na kojega se mogao spojiti jedan ili više klijenata te su oni putem dogovorenog protokola komunicirali. U takvoj komunikaciji bili smo izuzetno ograničeni. Primjerice, čak i kada se na poslužitelj moglo spojiti više klijenata oni su, nakon što su poslali jednu poruku poslužitelju, mogli samo čekati poruke od poslužitelja. Takvi primjeri su u praksi vrlo rijetki. U nastavku će biti riječi o jednom realnijem problemu. Ideja problema je:
Kreirajmo mrežnu aplikaciju koja će se sastojati od poslužitelja na kojeg će se moći spojiti više klijenata. Nakon što se spoje, klijenti će moći poslužitelju slati upite koji će se odnositi na to je li broj koji je klijent poslao poslužitelju prost ili složen. Poslužitelj treba omogućiti bilo kojem klijentu da u bilo koje vrijeme može poslati upit. Isto tako u bilo kojem trenutku na računalo se može spojiti novi klijent, itd.
Aktivnosti na strani klijenta nakon što se spoji na poslužitelj:
- čeka da korisnik upiše broj
- pošalje broj poslužitelju
- čeka da poslužitelj vrati poruku
- ispiše poruku na ekranu
Nakon što se uspostavi komunikacija između klijenta i poslužitelja na klijentskoj strani će se izvoditi jedna petlja koja će izvoditi gore opisane radnje.
Slika: Petlja koja se izvodi na strani klijenta nakon uspostave komunikacije s poslužiteljem
Na strani poslužitelja je situacija nešto kompleksnija jer poslužitelje treba istovremeno:
- čekati i prihvaćati nove klijente
- za svakog klijenta na socketu čekati poruku (broj), za dobiveni broj provjeriti je li prost te pripadnom klijentu vratiti odgovarajući poruku.
Ne postoji pravilo po kojem klijenti šalju zahtjeve poslužitelju. Ako je u nekom trenutku na poslužitelj spojeno 3 klijenta (klijent 1, klijent 2, klijent 3) tada oni primjerice zahtjeve mogu slati na sljedeći način:
· klijent 1: 7
· Klijent 1: 9
· klijent 2: 3
· klijent 1: 5
· klijent 2: 19
· klijent 2: 22
· klijent 3: 14
· …
Može se dogoditi da jedan klijent pošalje nekoliko zahtjeva za redom, neki drugi klijent može slati zahtjeve vrlo rijetko itd.
Dakle, nećemo moći imati jednu petlju unutar koje ćemo redom zaprimati zahtjeve od prvog klijenta, zatim drugog, potom trećeg itd. Dakle „istovremeno“ ćemo morati čekati poruke na socketima svih klijenata što znači da bi se za svakog klijenta na poslužitelju trebala neovisno jedna od druge odvijati petlja sljedećeg oblika.
Slika: Petlja koja bi se na poslužitelju istovremeno trebala odvijati za sve klijente
(za svakog klijenta po jedna ovakva petlja)
Istovremeno sa svim tim petljama poslužitelj treba još i izvoditi još jednu petlju unutar koje će prihvaćati nove klijente.
Problem: u našim dosadašnjim mrežnim programima petlje su bile na neki način beskonačne (izvode se dok je klijet spojen) i ne mogu slijediti jedna nakon druge (prva nikada nije gotova…). Iz istog razloga one ne mogu biti niti jedna unutar druge.
U ovom i sličnim primjerima koristit ćemo tzv. dretve (engl. Thread). Na dretvu možemo gledati kao na program koji je u mogućnosti pokrenuti se unutar nekog „glavnog“ programa te će se u tom slučaju izvoditi paralelno s glavnim programom.
Dakle, na strani poslužitelja imat ćemo:
- glavnu dretvu – čeka novog klijenta te ga prihvaća
- za svakog klijenta po jedna dretva – čeka poruku (broj) od klijenta; provjerava je li broj prost; vraća pripadnom klijentu odgovarajuću poruku.
Lako se uočava da će broj dretvi na poslužitelju biti ukupno n + 1, pri čemu je n broj klijenata koji su u nekom trenutku spojeni na poslužitelj.
Rad s dretvama – modul threading
Za rad s dretvama u Pythonu na raspolaganju nam je modul threading. Između ostaloga ovaj modul sadrži klasu Thread, čiji konstruktor dolazi u obliku:
Thread(target = ime_funkcije, args = (arg1, arg2,…))
Objekt tipa Thread predstavlja jednu dretvu. Dretva će u tom slučaju izvoditi neku funkciju (ime_funkcije), čije ćemo ime navesti kao parametar konstruktora klase Thread. U tom slučaju se navodi samo ime funkcije, bez parametara i zagrada. Ukoliko je funkcija definirana na način da ima nekih dodatnih parametara, oni se navode kao dodatni parametar konstruktora (args).
Nakon što smo kreirali objekt tipa Thread, nad njim se mogu izvoditi različite metode. Neke od češće korištenih metoda su:
-
start() – započinje izvođenje dretve unutar programa
-
join() – osigurava da program neće završiti sve dok pripadna dretva ne bude izvedena do kraja