Programming | Pascal » Kritikus régiók és monitorok

Datasheet

Year, pagecount:2004, 11 page(s)

Language:Hungarian

Downloads:33

Uploaded:November 07, 2010

Size:128 KB

Institution:
-

Comments:

Attachment:-

Download in PDF:Please log in!



Comments

No comments yet. You can be the first!

Content extract

http://www.doksihu Kritikus régiók és monitorok Kritikus régiók A kritikus régiók (critical region, CR) bevezetését Brinch Hansen javasolta 1972-ben. Ez az eszköz biztosan megvalósítja a kölcsönös kizárást, mert • a változót, amelyet kölcsönös kizárással akarunk elérni, csak úgy deklarálhatjuk, hogy a fordító hibát jelez a kritikus régión kívüli hozzáférési igény esetén; • a kritikus szakaszt védő műveletek most automatikusan generáltak (nem hibázhatunk, mint a szemaforok esetén). Noha a Pascal-FC-be ezt az eszközt nem építették be, érdemes megnézni a használatát Pascalszerű szintaxisban. A kritikus régió két komponensből áll: • deklarálnunk kell egy speciális változót (amely csak kölcsönös kizárással érhető el); • szükséges egy új strukturált kifejezés, amely a változón végrehajtandó műveletek kódolására szolgál. Szemléltető példák: var V: shared T; (* V a változó –

régió-azonosító, T a típus ) region V do S (* S tetszőleges kifejezés ) Ugyanazon azonosítóval ellátott kritikus régiók csak kölcsönös kizárással érhetők el, különböző azonosítóval ellátott kritikus régiók (például V1 és V2 változók) azonban párhuzamosan is futtathatók. A régió működése a következő: • Ha egy folyamat be szeretne lépni egy V kritikus régióba, akkor először meg kell szereznie a kölcsönös kizárási jogot (lock) V-re. • Ha ez nem sikerül, akkor a V-hez tartozó soron várakozásra kényszerül (blokkolt várakozás). A sort a régió azonosítójához rendeljük, tehát több azonos nevű régióhoz csak egy sor tartozik. • Ha a folyamat befejezte a tevékenységét a V kritikus régióban, akkor feloldja a kölcsönös kizárási zárlatot V-re, ezután indul egy blokkolt folyamat (ha van). A következő példa a díszkert probléma megoldását mutatja be kritikus régiók alkalmazásával: program gardens;

const nprocs = 3; (* 3 forgóajtó ) var COUNT: shared integer; turnstile: array[1.nprocs] of turnstype; i: integer; process type turnstype; var loop: integer; 1 http://www.doksihu begin for loop := 1 to 20 do region COUNT do COUNT := COUNT + 1; end; begin region COUNT do COUNT := 0; cobegin for i := 1 to nprocs do trunstile[i] coend writeln(COUNT); end. Érdemes megfigyelni, hogy a főprogramban a kezdőérték-adás csak kölcsönös kizárással valósítható meg (noha ez logikailag nem lenne szükséges), mivel csak ily módon érhető el a védett változó. Ez túlspecifikálás, kivédeni úgy tudnánk, ha a kezdőérték-adást a deklarációs részben is elvégezhetnénk. Feltételes kritikus régiók A kritikus régiók strukturáltabb és biztonságosabb megoldást adnak a kölcsönös kizárásra, mint a szemaforok, de nem alkalmasak a szinkronizációra (tehát gyengébb eszközök a szemaforoknál). „Felerősített” rokonaik, a feltételes kritikus régiók

(conditional critical region, CCR) azonban már rendelkeznek ugyanazzal a kifejező erővel, mint a szemaforok. A feltételes kritikus régiók Pascal-szerű szintaxisban a CR-hez hasonlóan definiálhatók, a hozzájuk tartozó új strukturált kifejezés pedig a következő (ezt az eszközt a Pascal-FC-be szintén nem építették be): region V when B do S B egy logikai kifejezés, ami általában V-re hivatkozik. A feltételes kritikus régió működése a következő: • Ha egy folyamat be szeretne lépni egy V feltételes kritikus régióba, akkor először meg kell szereznie a kölcsönös kizárási jogot (lock) V-re. • Ha ez nem sikerül, akkor a V-hez tartozó soron várakozásra kényszerül (blokkolt várakozás). A sort a régió azonosítójához rendeljük, tehát több azonos nevű régióhoz csak egy sor tartozik. • Ha a jog megszerzése sikerül, akkor a B logikai kifejezés kiértékelődik. Amennyiben ennek értéke igaz, akkor a folyamat beléphet a kritikus

szakaszba, különben el kell engednie a kölcsönös kizárási jogot, és blokkolt várakozó állapotba kerül. • Ha egy folyamat befejezte a tevékenységét a V feltételes kritikus régióban, akkor feloldja a kölcsönös kizárási zárlatot V-re, ezután indul egy blokkolt várakozó folyamat (ha van). A következő szemléltető példa egy lehetséges megoldást ad a termelő-fogyasztó problémára. program termfogy; const buffsize = .; type item = .; 2 http://www.doksihu bufftype = record nextin, nextout, count: integer; elements: array[1.buffsize] of item end; var buff: shared bufftype; process producer; begin repeat (* termelés ) region buff when buff.count < buffsize do (* új termék elhelyezése ) forever end; process consumer; begin repeat region buff when buff.count <> 0 do (* új termék kivétele ) forever end; begin (* buff inicializálása ) cobegin producer; consumer; coend end. A régión blokkolt folyamat elengedése Mivel egy feltételes

kritikus régióhoz két különböző típusú várakozó folyamat tartozhat, Brinch Hansen (1972) azt javasolta, hogy minden osztott változóhoz két várakozási sort rendeljenek. • Fő sor (main queue): azon folyamatok, amelyek nem jutottak be a kölcsönös kizárásba. • Esemény sor (event queue): azon folyamatok, amelyek bejutottak a kölcsönös kizárásba, de a logikai feltételük hamis volt. Egy shared változóhoz egy-egy sor tartozik. Ha egy folyamat befejezi a védett szakasz végrehajtását, akkor a többi várakozó folyamat logikai feltételét újra ki kell értékelni, mert közben megváltozhatott. Ezért Hansen azt javasolta, hogy minden folyamatot rakjunk át a fő sorba, és ezután újra versenyezniük kell a kölcsönös kizárási jog megszerzéséért: if "event queue nem üres" then "az összes folyamat átpakolása az event queue-ből a main queue-be" if "main queue nem üres" then "egy folyamat elengedése a main

queue-ből" else 3 http://www.doksihu "unlock az osztott változóra" Az algoritmusban pazarlásnak tűnik, hogy a logikai feltételek nagyon sokszor értékelődnek ki. Azonban ez a pazarlás „mérsékelt”, mert csak akkor történik újraértékelés, ha jó okunk van feltételezni, hogy a feltételek ténylegesen megváltoztak. Mások – a kevesebb folyamatközi kapcsolgatás érdekében – azt javasolták, hogy a védett szakasz végén a még aktív folyamat nézze meg az eseménysoron várakozó folyamatok logikai feltételeit, és ő(!) csak azokat értékelje ki újra, amelyek megváltoztak. Ez elméletileg nagyon elegáns megoldás, viszont a megvalósítással sok probléma lehet (például a vizsgálandó feltétel lokális változót is tartalmazhat, és a folyamatok nem feltétlenül látják egymás lokális változóit, tehát egy másik folyamatból a feltétel nem mindig értékelhető ki). A feltételes kritikus régiók korlátai Noha a

feltételes kritikus régiók a szemaforoknál „fejlettebb” eszközök, még mindig vannak fogyatékosságaik: • Szét vannak szórva a programszövegben (ideális esetben az összes kódot egy helyre kellene összegyűjteni, ami a védett változót használja). • A védett adatrész könnyen károsítható (ideális esetben a programozó számára csak előre megírt eljárásokon keresztül biztosítunk hozzáférést). • Nehéz őket hatékonyan implementálni (ellentétben a szemaforokkal). A 2. pontban javasolt védelem szükségességét egy banki problémával illusztráljuk A bank az ügyfelek adatait a következőképpen tárolja: const customermax = .; type accountnumber = 1.customermax; accountrec = record . balance: integer; (* egyenleg ) . end; var accounts: shared array[1.customermax] of accountrec; Ezt az adatszerkezetet sokféle folyamat használhatja, a folyamatokat a bank dolgozói és az ügyfelek futtatják különböző tevékenységeik végrehajtása

érdekében. Mj. Példánktól eltérően a valós életben a használat során szükséges kölcsönös kizárási jog nyilván nem az összes adatra, hanem csak a megfelelő rekordra vonatkozik (nálunk most az egyszerűség kedvéért egyszerre csak egy ügyfél hajthat végre tranzakciókat), de a főbb elvek ugyanazok. A tranzakciók eljárásokkal modellezhetők, például egy egyszerű átutalás a következő lehet: procedure xfer(fromacc, toacc: accountnumber; sum: integer); begin region accounts do begin with accounts[fromacc] do 4 http://www.doksihu balance := balance - sum; with accounts[toacc] do balance := balance + sum end end; A fő probléma azonban az, hogy az ügyfél nincs rákényszerítve arra, hogy az előre megírt eljárásokat használja. Bárki teljes joggal hozzáfér az egész struktúrához, így készíthet magának „saját” átutaló eljárást, és akár szándékosan, akár véletlenül megbonthatja az adatintegritást (pl. elmarad a balance

– sum ) A feltételes kritikus régiók felsorolt fogyatékosságainak kijavítására alkalmas új eszközök a monitorok. Monitorok A monitorokat Hoare 1974-ben vezette be, tulajdonságaik pontos leírásával. A Pascal-FC-ben lényegében ennek megfelelően használhatók. Ezeket az eszközöket több más párhuzamos nyelvben is megvalósították (pl. Concurrent Pascal, Pascal Plus, Mesa, Turing Plus) A monitorok felépítésének vázlata a következő: monitor „azonosító”; „export lista” „belső deklarációk” begin „monitortörzs” (* kezdőértékek ) end; Az export listában a kívülről is használható eljárásokat soroljuk fel, formális paraméterekkel. A deklarációk tartalmazhatnak konstans-, típus-, változó-, belső eljárás- és függvénydeklarációkat, de nem tartalmazhatnak folyamatokat vagy más monitorokat. A deklarációk többsége csak a monitor belsejében van hatályban, kivéve azokat, amelyeket az export listában is felsorolunk.

A Pascal-FC-ben csak az eljárások nevét lehet exportálni Az exportált eljárás kívülről aktuális paraméterekkel hívható: monitor azonosító.exportált eljárás azonosító[aktuális paraméterek] Az adatokhoz csak az exportált eljárásokon keresztül lehet hozzáférni, ez teljes ellenőrzést biztosít a műveletek felett. Mivel a monitor adatait kezelő kód a monitor belsejében van, így szép strukturált szerkezetet kapunk. A Pascal-FC-ben a monitortörzs kezdőérték-adásai a főprogram első sorának futtatása előtt végrehajtódnak, és ezután már az exportált eljárások is használhatók lesznek. A fordító garantálja, hogy az adatokhoz való hozzáférés a monitorok belsejében kölcsönös kizárással történik. Ha egy folyamat megpróbál elindítani egy olyan monitort, amelyet egy másik folyamat használ, akkor várakozásra kényszerül (blokkolt várakozás). A monitorhoz így egy várakozási sor tartozhat, ezt a sort monitor boundary

queue-nak nevezzük. A várakozási sor leggyakrabban a FIFO stratégiát használja, ez így van a Pascal-FC-ben is. A monitorok biztonságosan valósítják meg a kölcsönös kizárást, mert: • az adatszerkezet deklarációja a monitorban van, itt történik a kezdőértékek megadása is; • az adatszerkezet kívülről nem látható, az adatok csak előre megírt exportált eljárásokon keresztül módosíthatók, közvetlenül nem érhetők el; • a kölcsönös kizárással történő adathozzáférést a fordító kikényszeríti. 5 http://www.doksihu Számos szekvenciális és párhuzamos nyelvben találhatók olyan hasonló struktúrák, amelyek képesek „elrejteni” az adatokat ugyanúgy, mint a monitorok, de kölcsönös kizárás nélkül (module – Modula-2, package – Ada). Ezek a lehetőségek a moduláris programozás elveinek a megvalósítására szolgálnak. A díszkert probléma megoldása monitorokkal nagyon elegáns és meglehetősen egyszerű.

Példaprogram: GARDMON.PFC (40) A kritikus régiós megoldáshoz hasonlóan a főprogramban a kiíratás csak kölcsönös kizárással valósítható meg, noha ez logikailag nem lenne szükséges. Ennek oka ugyanaz, mint korábban: csak ily módon érhető el a monitor belseje. Feltétel-szinkronizáció monitorokkal Hasonlóan, ahogy a kritikus régiókat „fel kellett erősíteni”, jelenlegi állapotukban a monitorok sem alkalmasak még szinkronizációs feladatok megoldására. Hoare eredeti javaslata szerint a Pascal-FC-ben erre a condition változókat használjuk. Ezen változóknak nincs a programozó számára hozzáférhető értékük, hanem – kezdetben üres – FIFO sorokként működnek. Deklarációs példa: var c: condition; (* a monitor belsejében ) carray: array[1.10] of condititon; Ezek a változók azon folyamatok blokkolására szolgálnak, amelyek a monitorba belépve az adott helyzetben nem futtathatók (pl. fogyasztó folyamat üres puffer esetén) A

condition változókon két művelet értelmezett, a delay és resume. Ezek hatásukban a szemaforos waithez és signal-hoz hasonlóak, de vannak eltérések is Használható még az empty függvény (ld később), amely megnézi, hogy a condition változó várakozási sora üres-e. A delay művelet használata: delay(c) ahol c egy condition változó. A delay működése a következő: „monitor elengedése” „a hívó folyamat blokkolása c-n” A monitor elengedése a hívó részéről azért szükséges, hogy a monitor ne maradjon blokkolva, és így később más folyamat is beléphessen. Ez a hívó érdekét is szolgálja, mert különben sosem tudná befejezni a tevékenységét (példa: üres puffer esetében a fogyasztó csak abban bízhat, hogy előbb-utóbb lesz termelés). Lényeges különbség a szemaforos wait művelethez képest, hogy a delay esetében a folyamat mindig blokkolt lesz, míg szemaforos társa csak akkor blokkol, ha a szemafor értéke 0. A resume

művelet használata: resume(c) 6 http://www.doksihu Hatása a következő: „az első c-n várakozó folyamat indítása” vagy null (* üres tevékenység, ha nincs várakozó folyamat ) Eltérés a szemaforos signal művelethez képest, hogy a signal-nak mindig van valamilyen hatása. A resume működésével kapcsolatban fellép egy veszélyes probléma: ha egy folyamat a monitor belsejében a resume paranccsal felébreszt egy másik folyamatot (amely eredetileg a delay hatására blokkolódott), akkor az belép a monitorba. Ekkor sérülne a kölcsönös kizárás, hiszen az ébresztett folyamat és az ébresztő egyszerre lenne a monitorban! Ezt nyilván nem engedhetjük meg. A lehetséges megoldások: • az ébresztett vár a belépéssel, amíg az ébresztő befejezi a tevékenységét (resume-andcontinue, „ébreszt és folytat”); • az ébresztő azonnal befejezi a tevékenységét a monitor belsejében, és átadja a jogot a kölcsönös kizárásra az

ébresztettnek (immediate resumption, „azonnali ébredés” – többféle különböző változata ismert; ezt alkalmazták a Pascal-FC-ben is). Ezen lehetőségeket később még alaposabban is megvizsgáljuk. Addig a kérdést „elkerüljük” azzal, hogy egyelőre a resume parancsot csak a monitor eljárás végén lehet kiadni, így hallgatólagosan az immediate resumption stratégiát fogadjuk el. Monitorok alkalmazása klasszikus problémák megoldására Condition változók használatával a termelő-fogyasztó problémára általános pufferes megoldást adhatunk. A termelő és a fogyasztó a puffert a PLACE és a TAKE monitor eljárásokon keresztül éri el. Példaprogram: PCMON2.PFC (41) Az író-olvasó probléma megoldására a szemaforos változathoz hasonlóan az OPEN és a CLOSE műveleteket kell megvalósítani. Ezek monitor eljárások lesznek Az olvasó és az író folyamatok vázlata: process type reader; begin repeat READANDWRITE.OPEN(true); (*

hozzáférés az adatokhoz, átmásolhatja őket helyivé ) READANDWRITE.CLOSE(true); (* a helyi adatok használata ) forever end; (* reader ) process type writer; begin repeat (* új helyi adat készítése ) READANDWRITE.OPEN(false); (* az új helyi értékek írása az adatokhoz ) READANDWRITE.CLOSE(false); forever 7 http://www.doksihu end; (* writer ) Az OPEN és a CLOSE eljárások logikai paramétere jelzi, hogy a kérés írótól vagy olvasótól érkezik. Hoare javaslata alapján a következő megkötéseket tesszük: • új olvasó nem kezdhet dolgozni, ha van várakozó író; • írás művelet végén az olvasók kapnak elsőbbséget az írókhoz képest. Annak eldöntésére, hogy van-e várakozó folyamat, az empty függvényt használjuk. monitor READANDWRITE; export OPEN, CLOSE; var readercount: integer; activew: boolean; oktoread, oktowrite: condition; procedure OPEN(reading: boolean); begin if reading then begin if activew or not empty(oktowrite) then

delay(oktoread); readercount := readercount + 1; resume(oktoread) end else begin if activew or (readercount <> 0) then delay(oktowrite); activew := true end end; procedure CLOSE(reading: boolean); begin if reading then begin readercount := readercount - 1; if readercount = 0 then resume(oktowrite) end else begin activew := false; if not empty (oktoread) then resume(oktoread) else resume(oktowrite) end end; begin 8 http://www.doksihu activew := false; readercount := 0 end; (* READANDWRITE ) A szemaforos programmal összehasonlítva láthatjuk, hogy most jóval egyszerűbben tudtunk biztonságos és eleven megoldást adni. Megfigyelhető ugyanakkor, hogy a delay és a resume műveletek szétszórtan helyezkednek el a programban (ez már a condition változók fogyatékosságát jelzi), hasonlóan a szemaforos wait és a signal műveletekhez. Szemaforok szimulációja monitorokkal A szemaforok hibáinak kiküszöbölése céljából a kutatók folyamatosan új eszközöket

javasoltak az eljárások közti kommunikáció megoldására. Egy ilyen új eszköz általános felhasználásra akkor megfelelő, ha legalább olyan kifejező erővel rendelkezik, mint a szemafor. Ezt úgy lehet megmutatni, hogy szimuláljuk a szemafort az új szerkezettel A következő példaprogram egy bináris szemafort szimulál monitorok segítségével, amelyet most ebben az esetben a kölcsönös kizárás (díszkert probléma) megvalósítására használunk (ez elég, hiszen korábban megmutattuk, hogy a bináris és általános szemaforok azonos kifejező erővel rendelkeznek). Érdemes megfigyelni a shared változó szerepét: a szimuláció célja miatt példánkban globális, noha egy szép monitoros megoldásban logikailag a monitor belsejében kellett volna elhelyezni. Példaprogram: SEMMON.PFC (42) Mutassuk meg, hogy a monitorok is szimulálhatók a szemaforok segítségével! A szimuláció terjedjen ki a condition típusú változók kiváltására is. A lehetséges

resume stratégiák közül használjuk az „ébresztés és várakozás” módszert a lovagi sorral (ld. következő alfejezet) (2,5) A feladat eredményével együtt beláttuk, hogy a szemaforok és a monitorok egyformán erős eszközök. A resume lehetséges módszerei Eddig önfegyelmet tanúsítottunk azzal, hogy a resume műveletet csak a monitor eljárások végére helyeztük el. Ez azonban önmagában nem megbízható, az ébredésnél minden esetben garantálnunk kell azt, hogy biztosan csak egy folyamat lehessen egyidőben a monitor belsejében. Az alkalmazható módszereket részletesen megvizsgáljuk a) Ébresztés és folytatás (resume and continue) Ezt a technikát a Mesa nyelvben alkalmazták (Mitchell és társai, 1979). Lényege, hogy az ébresztő folyamat előbb befejezi tevékenységét a monitor belsejében, csak utána léphet be az ébresztett. A történés általában a következő módon írható le: (* a késleltetett folyamatban ) while not B then

delay(C) (* B most igaz ) . (* az ébresztő folyamatban ) „B igazra állítása” resume(C) 9 http://www.doksihu A késleltetett folyamatban azért szükséges a B logikai feltétel újrakiértékelése, mert az ébresztő még a resume után is megváltoztathatja B értékét. Az azonnali ébredés (immediate resumption) többféle módon is megvalósítható. b1) Ébresztés és kilépés (resume and exit) Ezt a technikát Brinch Hansen vezette be (Concurrent Pascal, 1975). Ilyenkor a resume hatására az ébresztő folyamat azonnal befejezi a tevékenységét a monitor belsejében, és átadja a jogot az ébresztettnek. Ekkor nyilván nincs szükség a logikai feltétel ismételt kiértékelésére b2) Ébresztés és várakozás (resume and wait) Ezzel a módszerrel az ébresztő folyamat szintén azonnal befejezi a tevékenységét a monitor belsejében, és átadja a jogot az ébresztettnek, de a monitor várakozási során blokkoljuk, és ha visszatér, onnan folytatja a

tevékenységét a monitor belsejében, ahol abbahagyta. Mivel az ébresztő rosszul járhat, ha más várakozó folyamatok megelőzik a bejutásban, ezért ezt a módszert Hoare (1974) finomította azzal, hogy a futási jogot lovagiasan átengedő folyamatokat egy magasabb prioritású várakozási sorba rakjuk, ahonnan előnnyel indulhatnak az ismételt bejutásért a többi várakozóhoz képest. A Pascal-FC-ben is ezt az elképzelést valósították meg, a kedvezményezett sor neve „lovagi sor” (chivalry queue). Így a monitor eleresztése után a következő történik: if „nem üres(ChivalryQueue)” then „folyamat indítása a ChivalryQueue-ből” else if „nem üres(MonitorBoundaryQueue)” then „folyamat indítása a MonitorBoundaryQueue-ből” else „a monitort szabadra állítjuk” A három technikát érdemes összehasonlítani a következő nézőpontok alapján. 1. Egyforma erősek-e ezek a módszerek? 2. Mennyire kényelmes a használatuk? 3. Mennyire

hatékonyak? 4. Mennyire könnyű bizonyítani a programkód helyességét? Andrews 1991-ben megmutatta, hogy az első kérdésre igen a válasz. Továbbá az esetek túlnyomó többségében a programok biztonságossága sem függ a választott resume módszertől. Különbség a többi kérdésre adott válaszban mutatható ki Tegyük fel, hogy a resume után az ébresztő folyamatban még további utasításokat kell végrehajtani. Ekkor a resume-and-exit technika esetében a kilépés után később majd egy újabb hívást kell alkalmazni a többi utasítás futtatása céljából. Így a monitor kód darabokra esik szét, és az export lista is bonyolultabbá válik. Ez a technika tehát kissé nehézkes a többihez képest. Hatékonysági nézőpontból a resume-and-wait technika kritizálható. Tegyük fel ugyanis, hogy a resume az utolsó utasítás az ébresztő monitorban. Ekkor az ébresztő később már csak azért fog belépni a monitorba, hogy utána azonnal kilépjen.

Ez nyilván nem kívánatos Ez a probléma nem lép fel a resume-and-continue módszer alkalmazásával, de ennek árát a logikai feltételek ismételt kiértékelésével fizetjük meg. 10 http://www.doksihu Hasonló a helyzet abban az esetben is, ha helyesség-bizonyítást akarunk végezni. A resumeand-wait technikánál a resume utáni első sorra csak nehezen határozhatók meg azok az állítások, amelyek a monitor belső állapotát írják le, hiszen az ébresztett folyamat sok mindent megváltoztathatott a monitor belsejében. Egymásba ágyazott monitorhívások A monitorok hatékonyan támogatják a moduláris programszerkesztést. Nagy párhuzamos rendszerekben a közös erőforrásokat gyakran monitorokkal implementálják, és ilyenkor a szoftvert általában rétegesen tagolják (layers), ahol az alacsonyabban elhelyezkedő rétegek szolgáltatásokat nyújtanak a magasabban levőknek (pl. operációs rendszerek) Ez esetben gyakran előfordul az, hogy egy

monitoreljárás belsejéből egy másik monitor eljárását hívjuk. Ezt a technikát beágyazott monitorhívásnak nevezzük. Az ilyen hívások több problémát vetnek fel. Tegyük fel, hogy egy folyamat az A monitor belsejéből hívja a B monitor valamely eljárását. • Megtartsuk-e a hívás alatt a kölcsönös kizárási jogot A belsejében? • Mi történjen, ha B-ben delay műveletet futtatunk? (B elengedése nyilván szükséges, de mi legyen A-val?) Négy különböző válasz adható: 1. A-ra fenntartjuk a kölcsönös kizárási jogot a beágyazott hívás alatt, és csak B-t engedjük el a delay hatására (gond: a blokkolás miatt csökken a párhuzamosság, deadlock veszély). 2. A-ra fenntartjuk a kölcsönös kizárási jogot a beágyazott hívás alatt, de mindkét monitort elengedjük a delay alatt (gond: nehéz a helyesség-bizonyítás). 3. A-t is elengedjük a beágyazott hívás alatt (gond: mint előbb) 4. Megtiltjuk a beágyazott hívásokat (könnyű

implementáció, de szegényesebb nyelv) Láthatjuk, hogy mindegyik módszernek vannak előnyei és hátrányai, semelyik sem teljesen kielégítő. A Pascal-FC-ben az első stratégiát alkalmazták Gyakorló feladatok a fejezethez F: Készítsünk megoldást az író-olvasó problémára feltételes kritikus régiók felhasználásával. Útmutató: kövessük a szemaforos megoldás lépéseit. (2,5-4) F: Készítsünk monitoros megoldást az étkező filozófusok problémájára úgy, hogy ne a körkörös lánc feloldásával, hanem más módszerrel akadályozzuk meg a deadlock kialakulását. (2-3) F: Írjunk monitoros programot a korábban megismert liftvezérlő implementálására. (3) 11