Az objektumok életvonalát a Simula olyan korutinokkal (kooperatív rutin) modellezi, melyek képesek egyes belépési pontjaikon átmenetileg megállni, majd folytatni működésüket. A (kvázi)párhuzamosságnak két szintje van. Az első szinten nem jelenik meg az idő, csupán a programozó gondolkodik "kvázi” párhuzamosan, a második szint vezeti be az idő fogalmát, ahol a programozó (felhasználói szemszögből) párhuzamos folyamatokkal dolgozik. Az első szinthez bármilyen osztályt felhasználhatunk, azonban a második szinthez a Simulation rendszerosztály Process osztályára kell építenünk. A lenti ábra szemlélteti egy X objektum korutinjának (törzsének) lehetséges állapotait. Ha egy törzs nem veszi fel a köztes állapotok egyikét sem, akkor először csatolt (attached), majd terminált (terminated) állapotú lesz. (Más objektum-orientált nyelvek objektumai mindig terminálnak). Egy terminált objektum a továbbiakban is használható marad, így el lehet érni a műveleteit és/vagy az attribútumait.
A Simula az alábbi lehetőségekkel (azaz minden osztályban elérhető metódusokkal) támogatja a kvázi párhuzamos rendszerekkel (QPS, QuasiParallel System) való munkát:
Detach: megállítja az aktuális objektum futását (az aktuális objektum az, amelyikre meghívtuk a Detach-et). Az aktuális objektum leválasztott (detached) állapotba kerül. A vezérlés átadódik arra a pontra, ahol az aktuális objektum létrejött vagy átvette a vezérlést.
Resume(Y): aktiválja a leválasztott (detached) Y objektumot, ami így éber (resumed), az aktuális objektum (az, ahonnan a metódust meghívtuk) pedig leválasztott (detached) állapotba kerül.
Call(Y): hasonlít a Resume-ra, de az Y objektum az aktuális objektumhoz lesz csatolva (attached). Az aktuális objektum ezzel leválasztott (detached) állapotba kerül, majd futása akkor folytatódik, amint az Y kerül leválasztott (detach) állapotba.
A következő programban három korutin szerepel: a két játékos és a főblokk. Meg kell jegyeznünk, hogy az első két lépés különleges kezdőlépés. Egy játékos csak a harmadik lépéstől kezdve nyerheti meg bizonyos valószínűséggel a játszmát: a Draw(0.3, Seed) egy logikai függvény, mely 0,3-es valószínűséggel ad vissza igaz értéket. Figyeljük meg, ahogy a főblokk létrehozza a két játékost, összeköti őket az ellenfél (Opponent) attribútumaikon keresztül és a Resume művelettel átadja a vezérlést a fehérnek. Ezután a játékosok felváltva lépnek, ami addig folytatódik amíg az egyikük meg nem nyeri a játszmát. Ez terminálja korutinjaikat és a vezérlés átadódik a főblokknak.
Ez a formázott kimenete a fenti programnak. A behúzás mutatja az objektumot, mely generálja a kimenetet (balról-jobbra rendre): főblokk, fehér (White), fekete (Black).
Creating Players, Starting the white one White's First Move Black's First Move White's Second Move Black's Second Move White's Move # 3 Black's Move # 3 White's Move # 4 Black's Move # 4 Finish: Black won in move 4
A következő programban három korutin szerepel: a két játékos és a főblokk. A különbséget ez és az előző program között a főprogram aktívabb szerepe jelenti. Itt nem a játékosok adják át egymásnak a vezérlést, hanem csak lépnek majd leválasztott állapotba kerülnek. A főprogram ismétlődően aktiválgatja őket, míg az egyikük meg nem nyeri a játékot. Figyeljük meg, hogy a Call parancs használatával a játékosok újra és újra a főblokkhoz csatolják magukat.
Ez a formázott kimenete a fenti programnak. A behúzás mutatja az objektumot, mely generálja a kimenetet (balról-jobbra rendre): főblokk, fehér (White), fekete (Black).
Creating Players, Starting the game White's First Move Black's First Move White's Second Move Black's Second Move White's Move # 3 Black's Move # 3 White's Move # 4 Black's Move # 4 White's Move # 5 Black's Move # 5 White's Move # 6 Finish: White won in move 6
A következő programban a fő osztály (Chess) két lokális osztályt deklarál: a Player-t és a Referee-t. A szimulációt a főprogram készíti elő a játékra, életvonala elején létrehozza és összekapcsolja a két játékost és a játékvezetőt. Mind a három objektum lecsatlakozik (detached lesz) és a fő osztály is terminál. A játékot az alább leírt programmal kell elindítani (amelyik prefixként használja a Chess-t). A prefixként való használat importál minden deklarációt és előkészíti a játékot. Vegyük észre, hogy a program csupán aktiválja a játékvezetőt és kiértékeli a játékot, de nem vezérli.
A következő programrészlet a fő osztályt prefixként használja:
Ez a formázott kimenete a fenti programnak. A behúzás mutatja az objektumot, mely generálja a kimenetet (balról-jobbra rendre): főblokk, fehér (White), fekete (Black).
Creating the Players and the Master Resuming the Master White's First Move Black's First Move White's Second Move Black's Second Move White's Move # 3 Black's Move # 3 White's Move # 4 Black's Move # 4 White's Move # 5 Black's Move # 5 White's Move # 6 Finish: White won in move 6
A Simulation rendszerosztály alapfogalma a Process – mely szekvenciális események (kódszegmensek által reprezentált) sorozata, melyeket passzív periódusok választanak el egymástól. A lenti ábra mutatja Simulation-ben deklarált osztályokat és azok metódusait. Az Event Notice (a Link leszármazottja) egy tervezett eseményt reprezentál, ami egy meghatározott időpontban (EvTime) hajtódik majd végre. A Proc az a folyamat, aminek a kódszegmense aktiválódni fog. Az SQS (a Head leszármazottja) pedig egy lista, melyben az események időrend szerint helyezkednek el. Ez a diszkrét szimulációs nyelvek úgynevezett naptárának felel meg. Koncepcionálisan ez egy rendezett láncolt lista, azonban belül általában egy jóval hatékonyabb bináris fa adatszerkezettel kerül megvalósításra. Természetesen ez a lista nem érhető el közvetlenül a felhasználói programból. Léteznek olyan, később tárgyalt ütemezők, melyek támogatják az idő figyelembe vételét és a folyamatok kölcsönös szinkronizációját. A felhasználói program folyamatai leszármazottai a közös ős Process osztálynak. Az első két metódus az állapotot teszteli, míg a másik kettő visszaadja a következő beütemezett eseményt és az aktiválási időt.
A Simulation rendszer szolgáltatásai az „alacsonyszintű” kváziparallel eljárásokkal (detach, resume, call) kerültek implementálásra, csak felhasználóbarátabb módon. A Simulation használója ”párhuzamosan gondolkodik”. A szimulációs modell dinamikáját a kölcsönösen kapcsolatba lépő párhuzamos folyamatok fejezik ki. Ez egy teljesen természetes megközelítés – lévén pontosan így működik a valós világ is.
Process objektumok állapotai:Az alábbi ábra a rendszer szimulációját mutatja. Ez egy bank absztrakciója, aholis az ügyfelek a pénztároshoz vezető sorokban várakoznak. A bankba belépő ügyfelek vételtelen, 1 és 3 perc között egyenletesen elosztott időközönként érkeznek. Minden pénztárosnak azonos, véletlenszerű kiszolgálási ideje van, normál eloszlással, 8 perc várható értékkel és 2 perc szórással. A szimuláció feladata hogy meghatározza az egy ügyfél által átlagosan a bankban eltöltött időt. A hasonló rendszerek szimulációja Simula-ban (pontosabban a Simula Simulation rendszer-osztályában) mindig a folyamat beazonosításával indul. Egy folyamat nyilván az ügyfelek előállítója lesz – ez ismétlődően generálja az ügyfeleket, rögzíti a beérkezési időket és véletlen ideig várakozik. A rendszer dinamikájának kifejezésére két logikai megközelítés létezik. Az egyik (amit ebben a példában használunk) alapja az aktív ügyfelek és a passzív pénztáros. A másik megközelítés – az aktív pénztáros és a passzív ügyfelek – a következő példában található meg.
Egy aktív ügyfél életvonalán a következő lépések követhetőek:A következő szimulációs modell szimulálja a fenti rendszert. Az elsődleges cél az volt, hogy a modell logikáját egy, a lehető legegyszerűbb programon mutassuk be. Igazi szimulációs modellek természetesen több paraméterrel rendelkezhetnek és többféle eredményt is adhatnak (mint például az átlagos és maximális sorhossz, stb.) A következő programban két folyamat van jelen a kísérlet teljes időtartalma alatt: a generátor és a főprogram, ami addig vár amíg a kísérlet le nem fut, majd megjeleníti a végeredményt. Ezen kívül változó számban ügyfél folyamatok is léteznek ideiglenesen. A statisztika frissítése után terminálnak. Figyeljük meg, milyen szabványos könyvtári eljárásokat használunk a véletlen számok előállítására. A legtöbb, gyakran használt eloszláshoz létezik megvalósítás a Simula-ban. A véletlen generálás egy egész értékről kerül inicializálásra. Így arra is lehetőségünk van, hogy minden véletlen változót külön kezeljünk, de arra is, hogy egy-egy közös véletlen folyamon belül generáljunk értékeket.
Ugyanúgy az előző példában leírt rendszert fogjuk szimulálni. A különbség az aktív szerver, ami ismétlődően szolgálja ki az ügyfeleket a sorból, amíg az ki nem ürül. Ezután a szerver passzív állapotba kerül. Az ügyfél feladata hogy aktiváljon egy üres szervert (ha van ilyen), majd álljon be annak sorába. Ez természetesen nem túl hatékony, de könnyen kezelhető. Az ügyfelek ezután a kiszolgálók által lesznek ismételten aktívak. Továbbiakban az ügyfelek már csak frissítik a statisztikát. A főprogram aktiválja az összes kiszolgálót, bár azonnal passzívak lesznek, mert a sorok üresek. Ezután a főprogram aktiválja a generátort majd vár, hogy befejeződjön a kísérlet.
Egy kvázi párhuzamos rendszer (QPS) Simula-ban alapvetően egy blokk (általában, de nem feltétlenül egy prefixelt blokk), ami a törzsében létrehoz néhány objektumot, aztán a főblokkal együtt felépíti a rendszer kvázi párhuzamos korutinjait. Mivel a Simula (eltérően például a Pascaltól) egy igazi blokkszerkezetes nyelv, ezért blokk (ami általában egy QPS-t jelent) bárhol elő tud fordulni a kódban kifejezésként. Ez azt jelenti hogy egy QPS tartalmazhat további (beágyazott) QPS-eket, melyek szintén tartalmazhatnak további QPS-eket, stb. Ez az egyszerű szabály lehetőséget ad hogy hihetetlenül összetett szerkezeteket hozzunk létre. A következő program az egymásba ágyazás egy lehetséges használatát mutatja be egy sakkjátszma vezérlésén keresztül.
Képzeljük el, hogy egy sakk játékos szimulálja a játszmát, következő lépésének lehetséges következményeit illetően. A következő program (a „Két mester” megközelítésre alapozva) vázol egy lehetséges megoldást. A Player osztálynak van egy TestStrategy nevű metódusa, mely előkészíti a döntést. Ez egy, a külsőhöz hasonló QPS megoldást implementál. Tartalmaz egy lokális TestPlayer osztályt, amely nagyon hasonlít a külső Player osztályra, a TestStrategy metódus kivételével (ez lehet hasonló, de a beágyazást le kell állítani egy szint után). A TestStrategy törzse a belső QPS, melynek eredményeit felső szint a Player osztálya használja fel.
A kvázi párhuzamosságnak Simula-ban két szintje van, és mindkettőben lehet implementálni beágyazott rendszereket. Az előzőekben példát láthattunk az alacsony szintű beágyazásra. A Simulation rendszer-osztály bevezeti az idő fogalmát. Ez azt jelenti, hogy a beágyazás, megjelennek belső (beágyazott) idők is. Az alapötletet az alábbi ábra mutatja. A Hold metódus x és z idejei a külső időre vonatkoznak, az y idő viszont már egy másik világ belső idejére vonatkozik. Tehát Simula lehetőségünk van a sci-fik távoli jövőjében található rendszerek szimulálására is, de van ennek a lehetőségnek gyakorlati alkalmazása is. Így például lehetőségünk van olyan rendszereket szimulálnunk, ahol a szimuláció a döntéshozás részeként szerepel. Gondoljuk át a következő helyzetet: adott egy bonyolult rendszer és szakértők egy csoportja, akik különböző javaslatokkal állnak elő, amelyek következményeit nem lehetséges triviális vagy analitikus módon kipróbálni. (Véletlenül nem pont ez a leggyakoribb eset?) A döntési javaslatok tesztelése és kiértékelése érdekében a szakértők szimulációs megfigyeléseket végeznek, majd összehasonlítják az eredményeket és kiválasztják a legígéretesebb döntés(eke)t. Tegyük fel, hogy most ezt szimuláljuk.
A lenti ábra mutatja a rendszer szimulációját. Ez egy bank absztrakciója, aholis az ügyfeleknek először ügyintézőkhöz vezető sorokban kell várakozniuk. Miután az ügyintéző kiszolgálta őket, az ügyfelek a pénztárosokhoz fáradnak, akikhez külön sor tartozik. A beérkezések közötti véletlen idő illetve az ügyintéző és a pénztáros véletlen hosszú kiszolgálási ideje ismert. Tegyük fel hogy van egy bizonyos számú munkatárs, akik képesek akár ügyintézőként, akár pénztárosként is dolgozni. A bank vezetősége le akarja tesztelni a munkatársak ügyintézővé illetve pénztárosa való beosztását a szokásos műszakokban: tétlen, foglalt és nagyon leterhelt, az ügyfelek beérkezése közti változó intervallumokkal.
A leghatékonyabb beosztás megtalálása érdekében a beágyazott szimuláció a következőképpen használható: minden periódus elején, a belső, ismétlődően futó szimuláció megvizsgálja az első szakasz eredményeit, az ügyintézők változó száma alapján (értelemszerűen ez a tartomány 1-től a lehető legtöbb munkatársig terjed). Ezen belső szimuláció eredményei alapján dönti el a vezetőség, hogy hány munkatárs dolgozzon ügyintézőként, és hány pénztárosként. A program leegyszerűsítése érdekében, ezt a döntést a felhasználó hozza meg, aki megkapja a belső szimuláció eredményeit, majd megkérjük, hogy adja meg az ügyintézők és pénztárosok számát. A döntést az első szakaszban eltöltött átlagos idő alapján kell meghozni, de érdekes lehet a leghosszabb rendszerben eltöltött idő is.
A következő „program” a szimuláció programjának vázlata (amit később részletezünk). Jegyezzük meg, hogy a belső blokk, amit a Simulation prefixel, minden lehetséges ügyintéző-pénztáros számra háromszor ismétlődően kerül lefuttatásra. A külső blokkéhoz hasonló osztályok deklarációit tartalmazza, a különbség az ügyfelek egyszerűbb viselkedésében mutatkozik meg, ugyanis itt csak egy kiszolgálási szakasz van. További beágyazás sincs. A szerkezet alapvetően nagyon hasonló az előző részben kifejtett „aktív ügyfél – passzív kiszolgáló” megközelítéshez.
A következő program a beágyazott szimuláció, a fenti szerkezettel.
Ez a szimuláció eredménye, a megadott számokkal együtt. Összesen 6 munkatársunk van, az ügyintézők és a pénztárosok beosztását a felhasználó adja meg. Figyeljük meg a legnagyobb rendszerben eltöltött időt. Egészen a harmadik periódusig elfogadható szinten marad, amikoris viszont 63.93 percre emelkedik (meg kell említenünk, hogy a teljes kiszolgálási idők átlaga 6+8=14 perc). Így világos, hogy a harmadik, nagyon leterhelt periódusban eggyel több ügyintéző érezhetően javítaná a rendszer teljesítményét. Az elméletet egy ugyanezen adatokkal történő újabb futtatással ellenőriztük 4 ügyintéző és 3 pénztáros mellett. Ez a következő eredményeket adta: a rendszerben töltött átlagos idő 19.67 perc ( 21.84 perc két pénztárossal), a rendszerben töltött leghosszabb idő pedig 36.58 percre csökkent, ahogy azt vártuk. A szimuláció végkövetkeztetéseképp tehát azt javasolja a vezetőségnek, hogy a harmadik periódusban eggyel több ügyintézőt osszanak be, máskor pedig a rendszer jól teljesít 6 munkatárssal is.
*** Results of internal simulation *** Period 1 Tellers Average time spent 1 181.404 2 52.691 3 6.132 4 5.979 5 5.972 6 5.972 Enter the number of tellers : 3 Enter the number of cashiers : 3 *** Report on external simulation *** 42 customers ready at time 120.00 Average time in system: 18.52 Maximum time in system: 25.95 Press Enter to Continue. *** Results of internal simulation *** Period 2 Tellers Average time spent 1 36.620 2 6.090 3 6.045 4 6.038 5 6.038 6 6.038 Enter the number of tellers : 2 Enter the number of cashiers : 4 *** Report on external simulation *** 91 customers ready at time 360.00 Average time in system: 17.29 Maximum time in system: 27.16 Press Enter to Continue. *** Results of internal simulation *** Period 3 Tellers Average time spent 1 205.290 2 103.226 3 7.937 4 5.993 5 5.974 6 5.972 Enter the number of tellers : 4 Enter the number of cashiers : 2 *** Report on external simulation *** 119 customers ready at time 480.00 Average time in system: 21.84 Maximum time in system: 63.93 Press Enter to Continue.