Kappale 2: Resource

muokkaa

Katso 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

muokkaa

Vaihtoehtoinen 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ä