U primjerima koji slijede koristi se modul threading
za upravljanje dretvama.
Funkcija za stvaranje dretve je Thread
. Npr.
opisnik = threading.Thread ( target = početna_funkcija, args = (arg1,arg2) )stvoriti će dretvu čija je početna funkcija
početna_funkcija
a parametri varijable arg1
i arg2
.
args
je tip podataka n-torka (tuple). Kada bi bio samo jedan argument, onda ga treba navesti sa zarezom args = (arg1,)
.
Stvorena dretva neće automatski krenuti s radom. Potrebno je pozvati metodu start
nad opisnikom stvorene dretve:
opisnik.start()
Glavna dretva može čekati na završetak stvorene dretve metodom join
:
opisnik.join()
U primjeru se stvara 5 dretvi koje svaka po 10000000 puta povećavaju zajedničku varijablu a
.
''' Osnovni primjer s dretvama. ''' import time, threading BR_DRETVI = 5 BR_ITERACIJA = 10000000 a = 1 def posao_dretve (id): ''' Početna funkcija za nove dretve ''' global a print ( "Kreće dretva " + str(id) ) for i in range(BR_ITERACIJA): a = a + 1 def main(): dretva = [None] * BR_DRETVI # polje za opisnike dretvi # stvaranje i pokretanje dretvi for i in range(BR_DRETVI): dretva[i] = threading.Thread ( target = posao_dretve, args = (i+1,) ) dretva[i].start() # čekanje na kraj rada svih dretvi for i in range(BR_DRETVI): dretva[i].join () print ( "Konačna vrijednost za a = " + str(a) + " (idealno " + str(BR_DRETVI*BR_ITERACIJA)+")" ) if __name__ == "__main__": main() # 2 x Ctrl+C prekida, ili Ctrl+\Obzirom da se varijabla
a
može mijenjati i paralelno, konačna vrijednost može biti manja od očekivane. To se može spriječiti sinkronizacijskim mehanizmima.
Drugi problem navedena primjer je prihvat signala. Signale u Pyhtonu prihvaća i obrađuje samo glavna dretva.
Međutim, ako je ona blokirana, kao u primjeru na pozivu join()
onda nastaju problemi (potrebno je 2 puta poslati signal sa Ctrl+C).
Bolje rješenje je definirati funkciju za prihvat ta signala, a u glavnoj dretvi pozivu join
dodati argument timeout
kojim će blokiranje biti vremenski ograničeno, ali se može pozivati u petlji. Npr. prema primjeru:
while opisnik.is_alive(): opisnik.join ( timeout = 1 )Time će glavna dretva čekati na završetak druge, ali ne beskonačno. U gornjem primjeru najviše sekundu. Ukoliko je signal došao u međuvremenu on će se obraditi nakon isteka jedne sekunde. Funkcija
is_alive()
će vratiti istinu (True
) ukoliko dretva još uvije radi (nije u međuvremnu završila s radom).
import time, threading, signal, sys kraj = False # na signal ova se varijabla mijenja BR_DRETVI = 3 # pretpostavljen broj dretvi BR_ITERACIJA = 5 # pretpostavljen broj iteracija def signal_kraj ( sig_num, frame ): ''' Na signal SIGINT (Ctrl+C) program završava ''' print ( "\nPrimljen signal za završetak ... ") global kraj kraj = True def posao_dretve (id): ''' Početna funkcija za nove dretve ''' i = 1 while not kraj: # dok signal za kraj nije došao print ( "Dretva " + str(id) + " iteracija " + str(i) ) time.sleep(1.0) i = i + 1 if i > BR_ITERACIJA: break def main(): signal.signal ( signal.SIGINT, signal_kraj ) global BR_DRETVI, BR_ITERACIJA if len(sys.argv) == 3: BR_DRETVI = int(sys.argv[1]) BR_ITERACIJA = int(sys.argv[2]) dretva = [None] * BR_DRETVI for i in range(BR_DRETVI): dretva[i] = threading.Thread ( target = posao_dretve, args = (i+1,) ) dretva[i].start() for i in range(BR_DRETVI): while dretva[i].is_alive(): dretva[i].join (timeout=1) if not kraj: print ( "Normalan završetak programa" ) if __name__ == "__main__": main()