VIII. Multitasking és segítôi

Az elôzôekben szó esett a multithreading-rôl, most egy kicsit pillantsunk be abba, hogy miként tudnak párhuzamosan futó programok, vagy programszálak egymással kommunikálni, egymáshoz alkalmazkodni. A "multitasking" alatt itt most szorosan csak arra gondolok, hogy egyszerre több különálló program fut, legyen az külön programban vagy egy adott program két programszálában, az itt tárgyalandó módszerek mindkét esetben alkalmazhatóak, noha fôként a különálló programok közötti kapcsolatban használatosak. (A thread-ek esetén - mivel ugyanabban a programban futnak - sokkal inkább használatos a változókon keresztüli adatforgalom és szinkronizáció annak egyszerûsége miatt.)

A cikk példaprogramjai a CD-n találhatóak, hosszúságukra való tekintettel. A cikk egy kissé el is tolódott afelé, hogy a példaprogramokat magyarázza; ez is tükrözi azt a személetet, hogy egy nyelvet leggyorsabban a programok megértésével és újabbak gyártásával lehet megtanulni. Ezek a programocskák nem csak nézegetésre jók, érdemes rajtuk próbálkozni, módosítgatni ôket, hogy kiderüljön hogy az egyes mechanizmusok mire jók.

A két feladatkör amivel a párhuzamosan futó programoknál szembe találjuk magunkat, az alábbi:

A futó programrészek szinkronizálása, amikor az egyik programrész nem futhat le addig, míg egy másik programrész be nem fejezôdött.

A programrészek közötti adatcsere.

Foglalkozzunk elôször az elsô problémával, kezdetnek ott is a legegyszerûbb (és legkevésbé hatékony) módszerrel, a flagfile-okkal. A flag szó zászlót jelent, általában a számítástechnikában pedig egy jelzést, itt és most a mehet-nem mehet jelzést. A flagfile egy file, melyet megvizsgál a kíváncsi programrész, és (például) amennyiben a file nem létezik, várakozik, és amikor a file megjelenik, akkor továbblép. Erre a mûködésre példa a FlagFile.cmd. A programocska bekér egy könyvtárnevet, majd elindítja a háttérben a FlagFil1.cmd-t, ami belekezd az olvasgatás hosszas mûveletébe. (Mielôtt ezt megteszi, biztos ami biztos alapon törli a flagfile-t, nehogy az elôzô, esetleg hibásan megállt futás miatt a már létezô file "becsapja".) Ezután bekér "valamit", ami csak azért került bele hogy mutassa, miközben a gép a "lassú" emberi beavatkozásra vár, addig a háttérben dolgozik a másik task. Ha a háttértask volt gyorsabb, akkor létrehozza a jelzôfile-t jelezve, hogy részérôl minden kész, és az átadandó adatot lementi az adatforgalomra szolgáló file-ba. Ha viszont mi voltunk a gyorsabbak, akkor a fôprogram megvárja míg az elôbbiek megtörténnek. Ha minden rendben megvan, akkor a fôprogram beolvassa a háttértask által elôállított adatokat és feldolgozza azokat.

Ennek a megoldásnak elônye, hogy egyszerû, átlátni is, elkészíteni is. Különösen akkor számít ez, ha a háttérben indított program nem OREXX, hanem akármilyen program, legyen az OS/2 alá írt C nyelvrôl fordított, vagy DOS alatti BATCH file. Mivel az OS/2 elég jó disk cache-el rendelkezik, még a sebesség sem olyan szörnyû, de azért a késôbbiekben bemutatott módszerekhez képest nagyságrendekkel lassabb. És ami a legfontosabb: az adatforgalomra használt file bizony néha igen nagy disk helyet igényelhet, valamint az egész mûvelet használja a disket, ami bizonyos esetekben (pl. power saving harddiskek vagy futtatás lassú médiáról, mint amilyen a floppy disk) hátrányos.

Ennél egy fokkal jobb megoldás az OREXX által nyújtott egyik lehetôség, a Global Environment használata. Néhány résszel ezelôtt volt szó az Environment objektumokról, és ott is a Global barátunkról, akinek a jellemzôje hogy a rendszeren futó összes OREXX program láthatja a tartalmát, valamint azonnal észlelheti a változásait. Ezt a módszert használja a GlobEnv.cmd, ami ugyanazt oldja meg, mint az elôzô példánk.

A módszer elsô elônye szinte azonnal látszik: itt igazi OREXX adatstruktúrákkal dolgozunk, ami fôleg az adatok átadásakor érezhetô, hiszen nem kell az Array objektumot szövegessé, majd újra visszaalakítani. Ha tisztán OREXX programokkal dolgozunk, talán ez a legkényelmesebb módja az adatcserének. Amit tárolásra használunk, az a rendszer memóriája, ez fôleg a nagyobb darab adatok cseréjénél tartandó észben.

Ha folyamatos adatcserét szeretnénk íly módon elérni, akkor sem kell kétségbe esni, hiszen az OREXX eddig megismert igencsak gazdag adatszerkezetei ismét segítségünkre sietnek: használjunk egy Global Environmentbe helyezett Queue objektumot verem vagy csô kialakításához, amibe az egyik task bedobálja az adatokat, míg a másik kiolvassa azokat. Ezt bemutatandó készült az MTaskQ.cmd, ami a háttérben indított MTaskQ1-tôl veszi át folyamatosan az adatokat. A SysSleep-eket itt is (és több helyen is) azért teszem be, mert így az események lassabban, jól láthatóan történnek, másrészt ha egy program folyamatosan fut, de nem csinál semmi hasznosat, akkor csak a rendszer erôforrásait zabálja, méghozzá igencsak kíméletlenül. (Ki lehet próbálni hogy mi történik akkor, ha az MTaskQ-ból kivesszük a SysSleep 1 másodperces pihenôidôit. Annyira meg fogja terhelni a rendszert, hogy a háttértask elindulása is komoly erôfeszítésekkel fog csak megtörténni.)

Legvégül térjünk rá azon módszerekre, melyek egyetlen célja az, hogy a fenti feladatokat minél jobban elvégezzék egy multitasking rendszerben, és ezek a queue-k és a szemaforok (semaphors).

A queue-k a REXX programokon belül használható adatcsatornák, melyekre a REXX programok rá tudnak csatlakozni, és abba adatokat behelyezni, illetve onnan adatokat tudnak kinyerni. A queue-k a REXX-bek lehetnek névtelenek vagy nevesítettek, az elôbbit csak a megnyitásakor adott azonosító (handle) azonosítja, az utóbbit minden program, aki ismeri a nevet, ezzel érheti el. Akármelyik mellett is döntünk, az RXQUEUE az utasítás amelyikkel használni tudjuk ôket. A kívánt queue-t valamelyik programrésszel létre kell hozni, ezután minden program tudja használni. (Az épp aktuálisnak kijelölt queue-t fogják a PUSH, PULL, QUEUED, stb. utasítások használni addig, míg vagy egy új queue-t ki nem jelölünk, vagy le nem zárjuk az összes "kézileg" létrehozott queue-t, amikor is ismét a STDIN/STDOUT lesz ezen utasítások célpontja. Az igazsághoz tartozik hogy minden programunk rendelkezik egy saját QUEUE: nevû darabbal, ami csak a saját és leszármazottai számára látható.) Bárki dobálhat bele adatokat, illetve vehet ki belôle, maga a queue nem végez semmiféle szinkronizálást. ("Aki kapja - marja.") Az adatok szimpla stringek, mindenféle struktúra nélkül. Ennek bemutatására készült a REXXQ.cmd nevû programocska. (Mûködése - míly meglepô - az elôzô példaprogramokkal igencsak megegyezô.)

Az IPC (Interprocess Communication, vagyis a futó mûveletek közötti kommunikáció) következô tagjai a pipe-ok (csövek) lennének, azonban az OREXX nem támogatja a pipe-ok létrehozását, noha a létrehozott pipe-okhoz a nevükön keresztül hozzá tud férni. Mivel OS/2 alatt a pipe-ok amúgy sem a sebességükrôl híresek, remélhetôleg nem jelent túl nagy veszteséget e lehetôség mellôzése. Ha mégis szükségünk lenne kezelésükre, érdemes a REXX-ek kibôvítô rutinkönyvtárakban szétnézni, ezek között kiemelendô az YDBAUTIL, amiben minden megvan, amire valaha is csak szükségünk lehet.

A következendô témánk a már említett szemaforok, melyek - mint a nevük is mutatja - arra jók, hogy programrészek futását engedélyezzék vagy tiltsák. A szemaforoknak két fô csoportja van, az Event Semaphores (event=esemény) illetve a MutEx Semaphores, melynek neve a "MUTually EXclusive", vagyis az "Egymást kölcsönösen kizáró" szemaforokat jelzi. Az Event szemaforok mûködése elég egyszerû: miután valaki létrehozta ôket, bárki hozzácsatlakozhat és bekapcsolhatja ("set") vagy kikapcsolhatja ("reset") attól függôen, hogy mit akar tudatni azokkal, akik a szemafor állapotát figyelik.

A másik csoport a MutEx szemaforok csoportja, melyek olyan események szabályozására készültek, melyekre több program áhítozik, de egyszerre csak egy végezheti el. Ilyenek például azok a tevékenységek, melyek egy meg-nem-osztható erôforrást használnak, vagyis egyszerre csak egy processz használhatja ôket.

Mindezekre példát a semaphor.cmd és a mutex.cmd mutat egy-egy egyszerû példán keresztül. Az elsô példa az Event szemaforokkal bûvészkedik: elindít három háttérprogramot, melyek véletlenszerûen beállított idô után bekapcsolják a szemafort, mire a fôprogram kilép. Látható hogy egyszerre mindegyik képes a szemafort kezelni, akár ki-bekapcsolni. A második példa a kizárólagos szemafort mutatja: három háttérfolyamat mindegyike megpróbálja megszerezni a szemafort, akinek sikerül, az dolgozik, majd elengedi a szemafor-használat jogát, hogy a többiek is megkaphassák.

Látható tehát hogy a szemaforok a szinkronizálás eszközei, és a szemaforokkal nem csak a REXX programjainkat, hanem tetszôleges, OS/2 alatti programokat is szinkronizálhatunk.

Gervai Péter
1997. 09. 23.
[ Elôzô lecke | Következô lecke | Tartalom ]