Simpy-Resource
Kappale 2: Resource
muokkaaKatso myös vaihtoehtoinen tapa tekstin lopusta.
Tehtävä 2
Tee ohjelma, joka simuloi autopesulan toimintaa. Autopesulassa on kaksi pesukonetta, joilla on 7 minuutin vakiomittainen pesuohjelma. Kahden pesun välillä on 1 minuutin välinen tekninen aikalisä. Autoja saapuu pesulaan satunnaisesti 5-7 minuutin välein. Jotta saadaan heti vähän ruuhkaa, niin generoidaan alkuun 5 autoa valmiiksi jonottamaan ennen kuin pesula käynnistyy.
Katsotaan sitten, miten voimme simuloida jotain resurssia, joita useampi prosessi haluaa omia. Tällaista simulointiresurssia tarvitaan esim. kaupan kassaa, huoltoaseman bensapumppua tms. simuloidessa. Simpy tarjoaa tähän Resource – menetelmän avuksi.
Resource on mikä tahansa resurssi, jonka yksittäinen prosessi voi varata itselleen haluamakseen ajaksi. Lähdetään tutustumaan resurssiin taas esimerkin kanssa.
Meillä on siis autopesula, jossa on 2 pesukonetta, joilla autoja pestään. Auton pesu varaa siis yhden koneen aina määrätyksi ajaksi. Koneella on 7 minuutin mittainen pesuohjelma ja kahden pesukerran väliin 1 minuutin mittainen "tekninen aikalisä". Autoja saapuu tasaiseen, satunnaiseen tahtiin 5-10 minuutin välein. Katsotaan miten pesulapyörii.
Tuodaan taas aluksi nuo tarvittavat kirjastot koodiin.
import random import simpy
Pesuun tuleva auto voidaan määritellä olioksi, jolla on tunnistetietona esim. numero, jotta tapahtumien seuranta olisi helpompaa. Tunnisteella pitää tietysti myös olla metodi, jolla tunniste saadaan esille.
class Auto: def __init__(self,numero): self.__numero=numero def get_numero(self): return self.__numero
Sitten tarvitsemme prosessin (generaattorin), joka luo pesuun tulevia autoja sovitun väliajan mukaisesti. Kun uusi auto-olio on luotu, sille voidaan käynnistää autokohtainen prosessi, joka huolehtii, että auto tulee pestyä. Itsenäinen prosessihan käynnistetään env.process(prosessi) käskyllä – prosessin nimi olkoon tässä tapauksessa pesu.
Tähän autojen generointi-prosessiin tuomme pääohjelmasta pesukone-nimisen parametrin, jonka välitämme edelleen pesu-prosessiin. Palaamme siihen hetken kuluttua.
Autojen generointi tapahtuu esim. seuraavalla ohjelmakoodilla:
def generoi_autoja(env,pesukone): i=1 while True: valiaika=random.randint(5,10) yield env.timeout(valiaika) auto=Auto(i) print('Auto', i, 'saapui pesulaan', env.now) env.process(pese(env, pesukone,auto)) i=i+1
Palatkaamme tuohon pesukone-parametriin. Simpyssä varattava resurssi voidaan määritellä joko olion metodissa tai pääohjelmassa seuraavasti:
resurssi=simpy.Resource(env,capacity=n)
Tuossa resurssi on muuttuja, johon ohjelmakoodissa viitataan, kun halutaan osoittaa ao. resurssia. capacity-muuttuja kertoo taas, kuinka monta identtistä resurssia on käytössä. Meidän koodissa resurssi kirjataan siis muodossa:
pesukone=simpy.Resource(env,capacity=2)
Resurssin varauspyyntö tehdään komennolla:
resurssinimi = resurssi.request()
jossa resurssi on Resource-määrittelyllä muodostettu, varattava resurssi ja resurssinimi tälle resurssille annettu nimi, jolla ohjelmassa voidaan viitata varattuun resurssiin Meidän tapauksessa varauspyyntömme voisi olla siten:
pesupaikka = pesukone. request()
Kun tällainen pyyntö on esitetty ja vaaditaan sen välitöntä suorittamista ennen ohjelman jatkumista yield resurssinimi – kehotuksella, ohjelma jää odottamaan, kunnes kyseinen resurssi vapautuu ja ohjelma pääsee jatkamaan suoritusta.
Kun resurssia on käytetty sen aikaa, mitä siitä tarvitaan, tulee se vapauttaa seuraavalle tarvitsijalle. Tämä tapahtuu komennolla:
resurssi.release(resurssinimi)
Ellei resurssia muista vapauttaa, kun sitä ei enää tarvitse, simulointi epäonnistuu varmasti. Ohjelmassamme pese-prosessi voisi siis näyttää seuraavalta:
def pese(env, pesukone,auto): pesupaikka = pesukone.request() yield pesupaikka print('Auto', auto.get_numero(),'pääsi pesuun', env.now) yield env.timeout(7) print('Auto', auto.get_numero(),'pääsi pesusta', env.now) pesukone.release(pesupaikka) #Vapautetaan pesupaikka yield env.timeout(1) #Tekninen aikalisä
Tehtävän annossa ohjeistettiin, että meidän pitäisi luoda alkuun hieman ruuhkaa viiden auto verran. Muokataan siis generoi_autoja – prosessia hieman seuraavaan muotoon:
def generoi_autoja(env,pesukone): for i in range(0,5): auto = Auto(i) env.process(pese(env, pesukone,auto)) "#Huom! Tässä vaiheessa ei ole vielä aikaa kulunut yhtään i=5 #Aloitetaan viidestä, kun noita autoja tuli jo luoduksi while True: valiaika=random.randint(5,10) yield env.timeout(valiaika) auto=Auto(i) print('Auto', i, 'saapui pesulaan', env.now) env.process(pese(env, pesukone,auto)) i=i+1
Sitten ei meiltä puutukaan muuta kuin pääohjelma, jossa luodaan yllä mainitulla tavalla tuo pesukone – resurssit ja muut tarvittavat osat.
env=simpy.Environment() pesukone=simpy.Resource(env,capacity=2) env.process(generoi_autoja(env,pesukone)) env.run(until=200)
Koko ohjelmakoodi näyttäisi siis tältä:
import simpy import random class Auto: def __init__(self,numero): self.__numero=numero def get_numero(self): return self.__numero def generoi_autoja(env,pesukone): for i in range(0,5): auto = Auto(i) env.process(pese(env, pesukone,auto)) i=5 #Aloitetaan viidestä, kun noita autoja tuli jo luoduksi while True: valiaika=random.randint(5,10) yield env.timeout(valiaika) auto=Auto(i) print('Auto', i, 'saapui pesulaan', env.now) env.process(pese(env, pesukone,auto)) i=i+1 def pese(env, pesukone,auto): pesupaikka = pesukone.request() yield pesupaikka print('Auto', auto.get_numero(),'pääsi pesuun', env.now) yield env.timeout(7) print('Auto', auto.get_numero(),'pääsi pesusta', env.now) pesukone.release(pesupaikka) yield env.timeout(1) #Tekninen aikalisä env=simpy.Environment() pesukone=simpy.Resource(env,capacity=2) env.process(generoi_autoja(env,pesukone)) env.run(until=100)
Lopputulema olisi seuraava:
Auto 0 pääsi pesuun 0 Auto 1 pääsi pesuun 0 Auto 0 pääsi pesusta 7 Auto 1 pääsi pesusta 7 Auto 5 saapui pesulaan 8 Auto 2 pääsi pesuun 8 Auto 3 pääsi pesuun 8 Auto 2 pääsi pesusta 15 Auto 3 pääsi pesusta 15 Auto 4 pääsi pesuun 16 Auto 5 pääsi pesuun 16 Auto 6 saapui pesulaan 17 Auto 4 pääsi pesusta 23 Auto 5 pääsi pesusta 23 Auto 7 saapui pesulaan 23 Auto 6 pääsi pesuun 24 Auto 7 pääsi pesuun 24 Auto 8 saapui pesulaan 30 Auto 6 pääsi pesusta 31 Auto 7 pääsi pesusta 31 Auto 8 pääsi pesuun 32 Auto 9 saapui pesulaan 39 ...
Kappale 2: Resource – vaihtoehtoinen tapa
muokkaaVaihtoehtoinen tapa hoitaa resurssin varaus ja samalla sen automaattinen vapauttaminen on käyttää komentoa:
with resurssi.request() as nimi:
jossa resurssi on varattava resurssi ja nimi tälle pyynnölle annettu nimi. Huomio, sekä piste että loppuun tuleva kaksoispiste. Sen jälkeen tuleva koodi pitää sisentää samalla tavalla kuin while- ja for- loop’eissa. Meidän tapauksessa varauspyyntömme olisi siten:
with pesukone.request() as pyynto:
Tällä menettelyllä ei tarvitse huolehtia resurssin vapauttamista, vaan resurssin vapauttaminen tapahtuu automaattisesti, kun varauspyynnön jälkeen sisennetty ohjelmakoodi on suoritettu. Ohjelmassamme pese-prosessi voisi siis näyttää myös seuraavalta:
def pese(env, pesukone,auto): with pesukone.request() as pyynto: yield pyynto print('Auto', auto.get_numero(),'pääsi pesuun', env.now) yield env.timeout(7) print('Auto', auto.get_numero(),'pääsi pesusta', env.now) yield env.timeout(1) #Tekninen aikalisä