5.3. Pythonin säikeet
Tässä luvussa perehdymme Pythonin säikeisiin. Lähdemateriaalina on:
- The Python Standard Library: 17.1. threading — Thread-based parallelism.
Huom!
Pythonissa oli aiemmin käytössä moduli thread
, joka toteutti yksinkertaisemman säiemekanismin. Tähän voi törmätä
vanhemmissa tietolähteissä, mutta sitä ei enää käytetä.
Luokka threading.Thread
Luokka threading.Thread esittää yksittäisiä Pythonin säikeitä.
Säikeen voi luoda kahdella tapaa, joko antamalla parametrina callable
-olion (esim. funktion) tai määrittämällä
uudelleen metodin Thread.run
. Esimerkki edellisestä tavasta:
thread = threading.Thread(target=aja_kertoma, args=(number,))
Tässä luodaan säie, joka käynnistyessään suorittaa funktionkutsun aja_kertoma(number)
.
Välikysymys:
Osaatko sanoa, miksi yllä kirjoitettiin args=(number,)
eikä args=(number)
?
Luokan Thread
voi periä, mutta ainoastaan luontimetodin __init__
sekä metodin run
saa määrittää
uudestaan. Muuten voi seurata sotkua.
Luokassa on määritetty seuraavat hyödylliset metodit:
start()
: käynnistää säikeen
run()
: säikeen varsinainen toiminta. Tämän voi siis määrittää uudestaan; muussa tapauksessa metodi kutsuu parametrina annettuatarget
oliota, joka tyypillisesti on funktio. Säikeen suoritus loppuu, kunrun
-metodista poistutaan.
join(timeout=None)
: Tätä metodia kutsuva säie odottaa, kunnes säie, johon kutsu kohdistuu päättyy. Jostimeout
on annettu, on sen oltavaNone
tai maksimiodotusajan sekunteina sisältävä liukuluku. Metodin paluuarvosta ei selviä, loppuiko säikeen suoritus oikeasti, joten tarvittaessa on käytettävä metodiais_alive
tämän selvittämiseen.Metodi
join
ei siis lopeta säikeen suoritusta.
is_alive()
: onko säie hengissä? Arvo on tosi metodinrun()
suorituksen ajan (tai oikeastaan alkaen hiukan ennen sen suoritusta ja päättyen hiukan sen suorituksen jälkeen).
Säikeen nimen saa attribuutista name
.
Esimerkki: Oman säieluokan tekeminen
#!/usr/bin/env python3
import time
import threading
class Tulostaja(threading.Thread):
def __init__(self, nimi):
super().__init__()
self.nimi = nimi
def run(self):
for i in range(10):
time.sleep(0.5)
print('{}: viesti {}'.format(self.nimi, i))
def main():
saieA = Tulostaja('Eka')
saieB = Tulostaja('Toka')
saieA.start()
saieB.start()
for i in range(10):
time.sleep(1)
print('{}, kierros {}'.format("main", i))
if __name__ =='__main__':
main()
Ajettaessa saadaan tulostus:
Eka: viesti 0
Toka: viesti 0
main, kierros 0
Eka: viesti 1
Toka: viesti 1
Eka: viesti 2
Toka: viesti 2
main, kierros 1
Eka: viesti 3
Toka: viesti 3
Eka: viesti 4
Toka: viesti 4
main, kierros 2
Eka: viesti 5
Toka: viesti 5
Eka: viesti 6
Toka: viesti 6
main, kierros 3
Toka: viesti 7
Eka: viesti 7
Toka: viesti 8
Eka: viesti 8
main, kierros 4
Toka: viesti 9
Eka: viesti 9
main, kierros 5
main, kierros 6
main, kierros 7
main, kierros 8
main, kierros 9
Lukot
Edellisessä esimerkissä säikeet toimivat näppärästi toisistaan riippumatta ja tulostivat siististi viestinsä. Tarkastellaan seuraavaa hieman muutettua esimerkkiä, jossa kaikki ei sujukaan enää nätisti.
Tulostukset sekaisin
#!/usr/bin/env python3
import time
import threading
import random
class Tulostaja(threading.Thread):
def __init__(self, nimi):
super().__init__()
self.nimi = nimi
def run(self):
for i in range(10):
time.sleep(0.5)
self.tulostaViesti(i)
def raskasLasku(self, n):
# Mielivaltainen aikaa vievä toimitus
sum = 0
for k in range(random.randint(0, 2**min(20, 4*n))):
sum += k
return sum
def tulostaViesti(self, i):
print('{}: viesti {} alkaa'.format(self.nimi, i))
for j in range(i):
tulos = self.raskasLasku(i)
print('{}: viesti {} osa {}: {}'.format(self.nimi, i, j, tulos))
print('{}: viesti {} loppuu'.format(self.nimi, i))
def main():
saieA = Tulostaja('Eka')
saieB = Tulostaja('Toka')
saieA.start()
saieB.start()
if __name__ =='__main__':
main()
Tämä tulostaa:
Eka: viesti 0 alkaa
Eka: viesti 0 loppuu
Toka: viesti 0 alkaa
Toka: viesti 0 loppuu
Eka: viesti 1 alkaa
Eka: viesti 1 osa 0: 36
Eka: viesti 1 loppuu
Toka: viesti 1 alkaa
Toka: viesti 1 osa 0: 36
Toka: viesti 1 loppuu
Eka: viesti 2 alkaa
Eka: viesti 2 osa 0: 11026
Eka: viesti 2 osa 1: 17020
Eka: viesti 2 loppuu
Toka: viesti 2 alkaa
Toka: viesti 2 osa 0: 16836
Toka: viesti 2 osa 1: 21736
Toka: viesti 2 loppuu
Toka: viesti 3 alkaa
Toka: viesti 3 osa 0: 946
Toka: viesti 3 osa 1: 42195
Toka: viesti 3 osa 2: 127765
Toka: viesti 3 loppuu
Eka: viesti 3 alkaa
Eka: viesti 3 osa 0: 2543640
Eka: viesti 3 osa 1: 4928230
Eka: viesti 3 osa 2: 4846941
Eka: viesti 3 loppuu
Toka: viesti 4 alkaa
Toka: viesti 4 osa 0: 531298503
Toka: viesti 4 osa 1: 222699960
Toka: viesti 4 osa 2: 63782865
Eka: viesti 4 alkaa
Eka: viesti 4 osa 0: 428849541
Eka: viesti 4 osa 1: 299158030
Eka: viesti 4 osa 2: 172766166
Eka: viesti 4 osa 3: 32124120
Eka: viesti 4 loppuu
Toka: viesti 4 osa 3: 1508350350
Toka: viesti 4 loppuu
Eka: viesti 5 alkaa
Toka: viesti 5 alkaa
Toka: viesti 5 osa 0: 17495441211
Toka: viesti 5 osa 1: 57451177878
Eka: viesti 5 osa 0: 258554202753
Eka: viesti 5 osa 1: 83696746953
Toka: viesti 5 osa 2: 403054292530
Eka: viesti 5 osa 2: 308705852890
Toka: viesti 5 osa 3: 538891726366
Toka: viesti 5 osa 4: 44228287236
Toka: viesti 5 loppuu
Eka: viesti 5 osa 3: 481286944386
Eka: viesti 5 osa 4: 10227002653
Eka: viesti 5 loppuu
Toka: viesti 6 alkaa
Eka: viesti 6 alkaa
Toka: viesti 6 osa 0: 271498646286
Eka: viesti 6 osa 0: 81622746666
Eka: viesti 6 osa 1: 356571124003
Toka: viesti 6 osa 1: 501646853835
Eka: viesti 6 osa 2: 256656391111
Toka: viesti 6 osa 2: 217530473436
Eka: viesti 6 osa 3: 234467644866
Eka: viesti 6 osa 4: 13995230556
Toka: viesti 6 osa 3: 542310613426
Eka: viesti 6 osa 5: 65879599591
Eka: viesti 6 loppuu
Toka: viesti 6 osa 4: 384692114085
Toka: viesti 6 osa 5: 392672118306
Toka: viesti 6 loppuu
Eka: viesti 7 alkaa
Eka: viesti 7 osa 0: 362910679176
Eka: viesti 7 osa 1: 50249413620
Toka: viesti 7 alkaa
Eka: viesti 7 osa 2: 302031966720
Eka: viesti 7 osa 3: 6953447628
Toka: viesti 7 osa 0: 431827589115
Eka: viesti 7 osa 4: 240989101381
Eka: viesti 7 osa 5: 3477571503
Eka: viesti 7 osa 6: 28090069776
Eka: viesti 7 loppuu
Toka: viesti 7 osa 1: 406143355815
Toka: viesti 7 osa 2: 94692774520
Toka: viesti 7 osa 3: 151325634453
Toka: viesti 7 osa 4: 113831528370
Toka: viesti 7 osa 5: 29178877951
Toka: viesti 7 osa 6: 72425325528
Toka: viesti 7 loppuu
Eka: viesti 8 alkaa
Eka: viesti 8 osa 0: 42919372653
Eka: viesti 8 osa 1: 3678203565
Eka: viesti 8 osa 2: 102650484651
Eka: viesti 8 osa 3: 405952309153
Eka: viesti 8 osa 4: 28350448140
Toka: viesti 8 alkaa
Toka: viesti 8 osa 0: 20418264240
Eka: viesti 8 osa 5: 233941338190
Toka: viesti 8 osa 1: 46714170630
Eka: viesti 8 osa 6: 200083472578
Eka: viesti 8 osa 7: 642199041
Eka: viesti 8 loppuu
Toka: viesti 8 osa 2: 301638046986
Toka: viesti 8 osa 3: 40168691641
Toka: viesti 8 osa 4: 7704066385
Toka: viesti 8 osa 5: 364656719001
Toka: viesti 8 osa 6: 100316530
Toka: viesti 8 osa 7: 375818056965
Toka: viesti 8 loppuu
Eka: viesti 9 alkaa
Eka: viesti 9 osa 0: 467945407405
Eka: viesti 9 osa 1: 25036842106
Eka: viesti 9 osa 2: 4584845161
Eka: viesti 9 osa 3: 269246263110
Eka: viesti 9 osa 4: 1484062440
Eka: viesti 9 osa 5: 66147028503
Toka: viesti 9 alkaa
Toka: viesti 9 osa 0: 120966326911
Toka: viesti 9 osa 1: 9729915751
Eka: viesti 9 osa 6: 449605625778
Eka: viesti 9 osa 7: 111872304636
Toka: viesti 9 osa 2: 344763396253
Eka: viesti 9 osa 8: 43205211946
Eka: viesti 9 loppuu
Toka: viesti 9 osa 3: 508579995696
Toka: viesti 9 osa 4: 389784899778
Toka: viesti 9 osa 5: 49765865841
Toka: viesti 9 osa 6: 232995612930
Toka: viesti 9 osa 7: 93810977281
Toka: viesti 9 osa 8: 142801566571
Toka: viesti 9 loppuu
Mitä tapahtui? Ensimmäiset viestit tulivat nätisti kokonaisina, mutta Tokan viesti 4 ei enää tullutkaan vaan Ekan viesti
putkahti väliin. Syy tähän on että metodi raskasLasku
alkoi isommilla parametrin arvoilla viedä pitemmän aikaa ja
viestin osien tulostuksen välit tulivat niin pitkiksi, että Python ehti vaihtaa suoritettavaa säiettä.
Tulostukset taas järjestyksessä
Yksinkertainen korjaus on käyttää tulostamisen ympärillä lukkoa. Seuraavassa teemme sen Pythoninwith
-lausetta käyttäen.
#!/usr/bin/env python3
import time
import threading
import random
tulostusLukko = threading.Lock()
class Tulostaja(threading.Thread):
def __init__(self, nimi):
super().__init__()
self.nimi = nimi
def run(self):
for i in range(10):
time.sleep(0.5)
self.tulostaViesti(i)
def raskasLasku(self, n):
# Mielivaltainen aikaa vievä toimitus
sum = 0
for k in range(random.randint(0, 2**min(20, 4*n))):
sum += k
return sum
def tulostaViesti(self, i):
with tulostusLukko:
print('{}: viesti {} alkaa'.format(self.nimi, i))
for j in range(i):
tulos = self.raskasLasku(i)
print('{}: viesti {} osa {}: {}'.format(self.nimi, i, j, tulos))
print('{}: viesti {} loppuu'.format(self.nimi, i))
def main():
saieA = Tulostaja('Eka')
saieB = Tulostaja('Toka')
saieA.start()
saieB.start()
if __name__ =='__main__':
main()
Nyt saamme taas siistin tulostuksen:
Eka: viesti 0 alkaa Eka: viesti 0 loppuu Toka: viesti 0 alkaa Toka: viesti 0 loppuu Eka: viesti 1 alkaa Eka: viesti 1 osa 0: 10 Eka: viesti 1 loppuu Toka: viesti 1 alkaa Toka: viesti 1 osa 0: 1 Toka: viesti 1 loppuu Eka: viesti 2 alkaa Eka: viesti 2 osa 0: 3 Eka: viesti 2 osa 1: 253 Eka: viesti 2 loppuu Toka: viesti 2 alkaa Toka: viesti 2 osa 0: 171 Toka: viesti 2 osa 1: 13861 Toka: viesti 2 loppuu Eka: viesti 3 alkaa Eka: viesti 3 osa 0: 220780 Eka: viesti 3 osa 1: 3073960 Eka: viesti 3 osa 2: 1648020 Eka: viesti 3 loppuu Toka: viesti 3 alkaa Toka: viesti 3 osa 0: 993345 Toka: viesti 3 osa 1: 692076 Toka: viesti 3 osa 2: 2137278 Toka: viesti 3 loppuu Eka: viesti 4 alkaa Eka: viesti 4 osa 0: 1557071110 Eka: viesti 4 osa 1: 1126391916 Eka: viesti 4 osa 2: 555994531 Eka: viesti 4 osa 3: 27966 Eka: viesti 4 loppuu Toka: viesti 4 alkaa Toka: viesti 4 osa 0: 582240750 Toka: viesti 4 osa 1: 53690703 Toka: viesti 4 osa 2: 35099631 Toka: viesti 4 osa 3: 136727916 Toka: viesti 4 loppuu Eka: viesti 5 alkaa Eka: viesti 5 osa 0: 269439292486 Eka: viesti 5 osa 1: 72155736786 Eka: viesti 5 osa 2: 202769537790 Eka: viesti 5 osa 3: 443482789366 Eka: viesti 5 osa 4: 11134871065 Eka: viesti 5 loppuu Toka: viesti 5 alkaa Toka: viesti 5 osa 0: 168787762578 Toka: viesti 5 osa 1: 440794302985 Toka: viesti 5 osa 2: 335013279076 Toka: viesti 5 osa 3: 1030784310 Toka: viesti 5 osa 4: 478985079420 Toka: viesti 5 loppuu Eka: viesti 6 alkaa Eka: viesti 6 osa 0: 22866659731 Eka: viesti 6 osa 1: 153044164378 Eka: viesti 6 osa 2: 484906164445 Eka: viesti 6 osa 3: 503257789378 Eka: viesti 6 osa 4: 241407218976 Eka: viesti 6 osa 5: 426452445156 Eka: viesti 6 loppuu Toka: viesti 6 alkaa Toka: viesti 6 osa 0: 120837000606 Toka: viesti 6 osa 1: 232901418753 Toka: viesti 6 osa 2: 198460395136 Toka: viesti 6 osa 3: 19079932185 Toka: viesti 6 osa 4: 63117801456 Toka: viesti 6 osa 5: 8244371436 Toka: viesti 6 loppuu Eka: viesti 7 alkaa Eka: viesti 7 osa 0: 143530372090 Eka: viesti 7 osa 1: 198570033145 Eka: viesti 7 osa 2: 116765039625 Eka: viesti 7 osa 3: 152919154851 Eka: viesti 7 osa 4: 112353879561 Eka: viesti 7 osa 5: 255110459253 Eka: viesti 7 osa 6: 224173040491 Eka: viesti 7 loppuu Toka: viesti 7 alkaa Toka: viesti 7 osa 0: 21743110311 Toka: viesti 7 osa 1: 139327327503 Toka: viesti 7 osa 2: 196379891865 Toka: viesti 7 osa 3: 10106225535 Toka: viesti 7 osa 4: 549433424046 Toka: viesti 7 osa 5: 515159698581 Toka: viesti 7 osa 6: 478735528260 Toka: viesti 7 loppuu Eka: viesti 8 alkaa Eka: viesti 8 osa 0: 365407770003 Eka: viesti 8 osa 1: 101047376475 Eka: viesti 8 osa 2: 369035425605 Eka: viesti 8 osa 3: 281095896115 Eka: viesti 8 osa 4: 79074890040 Eka: viesti 8 osa 5: 28910385570 Eka: viesti 8 osa 6: 106325741511 Eka: viesti 8 osa 7: 402239470128 Eka: viesti 8 loppuu Toka: viesti 8 alkaa Toka: viesti 8 osa 0: 61430557870 Toka: viesti 8 osa 1: 1057885003 Toka: viesti 8 osa 2: 549432375778 Toka: viesti 8 osa 3: 263479560240 Toka: viesti 8 osa 4: 113340604105 Toka: viesti 8 osa 5: 299961914878 Toka: viesti 8 osa 6: 524271104136 Toka: viesti 8 osa 7: 8371209528 Toka: viesti 8 loppuu Eka: viesti 9 alkaa Eka: viesti 9 osa 0: 348825039885 Eka: viesti 9 osa 1: 102659546881 Eka: viesti 9 osa 2: 377857327221 Eka: viesti 9 osa 3: 94548348378 Eka: viesti 9 osa 4: 137480113161 Eka: viesti 9 osa 5: 150796315725 Eka: viesti 9 osa 6: 69799875265 Eka: viesti 9 osa 7: 11175648256 Eka: viesti 9 osa 8: 60448166551 Eka: viesti 9 loppuu Toka: viesti 9 alkaa Toka: viesti 9 osa 0: 193303629753 Toka: viesti 9 osa 1: 307235886903 Toka: viesti 9 osa 2: 350897870778 Toka: viesti 9 osa 3: 157308050871 Toka: viesti 9 osa 4: 293153222071 Toka: viesti 9 osa 5: 88162414005 Toka: viesti 9 osa 6: 128547569535 Toka: viesti 9 osa 7: 44789496753 Toka: viesti 9 osa 8: 18068339656 Toka: viesti 9 loppuu
Ehtomuuttujat
Seuraavassa tarkastellaan tuottaja-kuluttaja -ongelmaa. Tuottajat valmistavat tuotteita, joita kuluttajat kuluttavat. Tuottajien ja kuluttajien välissä on rajallisen kokoinen puskuri, jota käytetään välivarastona tuotetuille mutta vielä kuluttamattomille tuotteille. Kuluttaja ei voi kuluttaa tuotteita, jos niitä ei ole tarjolla puskurissa ja vastaavasti tuottaja ei voi tuottaa lisää tuotteita, jos puskuri on jo täynnä.
Puskuri voidaan määrittää esimerkiksi seuraavasti:
class Buffer:
def __init__(self, capacity):
self.capacity = capacity
self.contents = []
def isFull(self):
return len(self.contents) == self.capacity
def isEmpty(self):
return len(self.contents) == 0
def addItem(self, item):
if self.isFull():
raise BufferException("Buffer is already full")
else:
self.contents.append(item)
def removeItem(self):
if self.isEmpty():
raise BufferException("Buffer is empty")
else:
item = self.contents.pop(0)
return item
def print(self):
print("Buffer has {} items:".format(len(self.contents)))
for item in self.contents:
print(" {}".format(item))
print()
Tuottajat ja kuluttajat on kätevää esittää säikeinä, jolloin ne voivat toimia rinnakkain. Ensimmäinen ongelma on, miten pidetään huolta siitä, että puskuria käytetään hallitusti eivätkä säikeet sotke sen sisältöä yrittämällä päivittää sitä samanaikaisesti. Tämän voisi helposti ratkaista käyttämällä lukkoa, joka pitää saada haltuun ennen kuin saa päivittää puskuria. Nyt on kuitenkin toinen ongelma: mitä jos tuottaja on saanut lukon haltuunsa ja yrittää päivittää puskuria, mutta se on jo täynnä? Tuottajan lienee syytä vapauttaa lukko, mutta milloin sen on mielekästä yrittää uudestaan päivitystä?
Tämän ongelman ratkaisemiseen käytetään niin sanottuja ehtomuuttujia (engl. condition variable). Ehtomuuttujaan
liittyy lukon lisäksi operaatiot wait
ja notify
. Jos tuottajasäie ei voi päivittää puskuria, koska se on täysi,
kutsuu se ehtomuuttujan metodia wait
ja jää odottamaan. Tämä odottaminen ei syö järjestelmän resursseja, koska
odottava säie on passiivisena ehtomuuttujaan liittyvässä jonossa. Kun joku kuluttajasäie on poistanut puskurista
tuotteen, kutsuu se vastaavasti ehtomuuttujan metodia notify
. Tällöin Python ottaa jonkun wait
-kutsulla
odottamaan siirtyneen säikeen ja antaa sen jatkaa suoritusta. Samalla tämä säie saa haltuunsa ehtomuuttujaan liittyvän
lukon, josta se automaattisesti luopui kutsuessaan metodia wait
.
Seuraavassa tämä on koottu kolmeen luokkaan, Producer
, Consumer
sekä Factory
, joka kokoaa yhteen joukon
tuottajia ja kuluttajia sekä puskurin ja tarvittavan ehtomuuttujan. Tuottaja suorittaa metodissa run
silmukkaa,
jossa tuotetaan uusi tuote metodilla produceItem
. Tämä jatkuu, kunnes tuo metodi heittää poikkeuksen
FactoryStopped
. Metodissa produceItem
aluksi tehdään tuote (tässä käytetään metodia time.sleep()
, jotta
saadaan näennäisesti aikaa kulumaan tuotteen tekemiseen). Sitten with
-lauseen avulla otetaan ehtomuuttujaan
liittyvä lukko haltuun ja tarkistetaan while
-silmukassa onko puskuri täysi. Jos se on, kutsutaan metodia wait
,
jolloin lukko vapautuu ja säie jää odottamaan. Odottamista ei kuitenkaan tehdä, jos tehdas ei enää ole käynnissä (eli
factory.isRunning
on False
). Odottaminen päättyy, kun joku toinen säie kutsuu ehtomuuttujan metodia notify
ja Python valitsee tuottajan suoritukseen. While-silmukassa tarkistetaan uudestaan, onko puskurissa nyt tilaa ja jos on
(tässä tapauksessa notify
-metodia oli kutsunut joku kuluttaja) ja tehdas on yhä käynnissä, lisätään tuote
puskuriin. Jos tilaa ei vieläkään ole (tässä tapauksessa notify
-metodia oli kutsunut joku tuottaja) jatketaan
odottamista. Lopuksi kutsutaan ehtomuuttujan metodia notify
. Tällöin mahdollisesti odottava toinen tuottaja tai
kuluttaja pääsee jatkamaan.
Kuluttaja toimii samankaltaisesti, mutta silmukkaehtona on että puskuri ei ole tyhjä. Tehtaan pysähtyminen tarkastetaan
aina silmukkaehdossa ja jos silmukasta poistuttaessa tehdas on pysähtynyt, heitetään poikkeus
FactoryStoppedException
.
class Producer(threading.Thread):
def __init__(self, name, factory, productionDelay):
super().__init__()
self.name = name
self.factory = factory
self.productionDelay = productionDelay
self.itemCount = 0
def produceItem(self):
time.sleep(self.productionDelay)
item = "<item {} by {}>".format(self.itemCount, self.name)
self.itemCount += 1
buffer = self.factory.buffer
cv = self.factory.conditionVariable
with cv:
while buffer.isFull() and factory.isRunning:
cv.wait()
if factory.isRunning:
print("Adding {} to buffer".format(item))
buffer.addItem(item)
buffer.print()
cv.notify()
else:
raise FactoryStoppedException
def run(self):
try:
while True:
self.produceItem()
except FactoryStoppedException:
return
class Consumer(threading.Thread):
def __init__(self, name, factory, consumptionDelay):
super().__init__()
self.name = name
self.factory = factory
self.consumptionDelay = consumptionDelay
def consumeItem(self):
buffer = self.factory.buffer
cv = self.factory.conditionVariable
with cv:
while buffer.isEmpty() and factory.isRunning:
cv.wait()
if factory.isRunning:
item = buffer.removeItem()
print("Consumer {} removed {} from buffer".format(self.name, item))
buffer.print()
cv.notify()
else:
raise FactoryStoppedException
time.sleep(self.consumptionDelay)
def run(self):
try:
while True:
self.consumeItem()
except FactoryStoppedException:
return
class Factory(threading.Thread):
def __init__(self, bufferCapacity, producerDefinitions, consumerDefinitions, runTime):
super().__init__()
self.buffer = Buffer(bufferCapacity)
self.conditionVariable = threading.Condition()
self.producers = [ Producer(name, self, delay) for name, delay in producerDefinitions ]
self.consumers = [ Consumer(name, self, delay) for name, delay in consumerDefinitions ]
self.runTime = runTime
self.isRunning = True
def run(self):
self.running = True
for thread in self.consumers + self.producers:
thread.start()
time.sleep(self.runTime)
with self.conditionVariable:
self.isRunning = False
print("Stopping factory")
self.buffer.print()
self.conditionVariable.notify_all()
for thread in self.consumers + self.producers:
thread.join()
Tehdään vielä pääohjelma, jossa luodaan tehdas
if __name__ == "__main__":
factory = Factory(4, [ ("p1", 1), ("p2", 1), ("p3", 1), ("p4", 1) ], [ ("c", 3) ], 20)
factory.start()
Koko ohjelma on tiedostossa ../_static/example-files/k05/producer_consumer.py.
Kun se suoritetaan, saadaan seuraava tulos:
Adding <item 0 by p1> to buffer
Buffer has 1 items:
<item 0 by p1>
Adding <item 0 by p2> to buffer
Buffer has 2 items:
<item 0 by p1>
<item 0 by p2>
Adding <item 0 by p3> to buffer
Buffer has 3 items:
<item 0 by p1>
<item 0 by p2>
<item 0 by p3>
Adding <item 0 by p4> to buffer
Buffer has 4 items:
<item 0 by p1>
<item 0 by p2>
<item 0 by p3>
<item 0 by p4>
Consumer c removed <item 0 by p1> from buffer
Buffer has 3 items:
<item 0 by p2>
<item 0 by p3>
<item 0 by p4>
Adding <item 1 by p1> to buffer
Buffer has 4 items:
<item 0 by p2>
<item 0 by p3>
<item 0 by p4>
<item 1 by p1>
Consumer c removed <item 0 by p2> from buffer
Buffer has 3 items:
<item 0 by p3>
<item 0 by p4>
<item 1 by p1>
Adding <item 1 by p2> to buffer
Buffer has 4 items:
<item 0 by p3>
<item 0 by p4>
<item 1 by p1>
<item 1 by p2>
Consumer c removed <item 0 by p3> from buffer
Buffer has 3 items:
<item 0 by p4>
<item 1 by p1>
<item 1 by p2>
Adding <item 1 by p4> to buffer
Buffer has 4 items:
<item 0 by p4>
<item 1 by p1>
<item 1 by p2>
<item 1 by p4>
Consumer c removed <item 0 by p4> from buffer
Buffer has 3 items:
<item 1 by p1>
<item 1 by p2>
<item 1 by p4>
Adding <item 1 by p3> to buffer
Buffer has 4 items:
<item 1 by p1>
<item 1 by p2>
<item 1 by p4>
<item 1 by p3>
Consumer c removed <item 1 by p1> from buffer
Buffer has 3 items:
<item 1 by p2>
<item 1 by p4>
<item 1 by p3>
Adding <item 2 by p1> to buffer
Buffer has 4 items:
<item 1 by p2>
<item 1 by p4>
<item 1 by p3>
<item 2 by p1>
Consumer c removed <item 1 by p2> from buffer
Buffer has 3 items:
<item 1 by p4>
<item 1 by p3>
<item 2 by p1>
Adding <item 2 by p2> to buffer
Buffer has 4 items:
<item 1 by p4>
<item 1 by p3>
<item 2 by p1>
<item 2 by p2>
Consumer c removed <item 1 by p4> from buffer
Buffer has 3 items:
<item 1 by p3>
<item 2 by p1>
<item 2 by p2>
Adding <item 2 by p4> to buffer
Buffer has 4 items:
<item 1 by p3>
<item 2 by p1>
<item 2 by p2>
<item 2 by p4>
Stopping factory
Buffer has 4 items:
<item 1 by p3>
<item 2 by p1>
<item 2 by p2>
<item 2 by p4>