Telefonkönyves példánkra ráunva, KUMM Visual C-ben írt Thread-es példáján felbuzdulva mi is thread-ekkel foglalkozunk két cikk erejéig.

Windows-ban majd' minden program több szálból áll, mindenegyes szál egy részprogramként értendő, s ezeket a részprogramokat külön - külön futtatja az operációs rendszer. Most a szálak létrehozásának és kezelésének elméletével ismerkedhetünk meg.

Mit, miért, hogyan?

A szálakat az adott programon belül az operációs rendszer külön hajtja végre, továbbá a program írása során is már látható módon elkülönülnek az egyes szálak. Mivel az operációs rendszer külön hatja végre az egyes szálakat, így minden egyes szálra külön számítódik a ráeső processzor-idő (végrehajtási időszelet), sőt a szálaknak külön - külön prioritást (fontossági szintet) is adhatunk. Ez kedvezően hangzik, hisz így programunk több processzoridőhöz juthat (a szálak arányában), gondolnánk először. Valóban ennek is meg van az előnye, hisz egyes részfeladatokat, amik nagyon lelassítanák programunkat külön szál(ak)ba a program másodlagos vagy további szálaiba tehetőek. (Ugyanis amikor nem használunk szálakat, programunk egy-szálú felépítésű, ekkor csak a fő szál - main thread - él.) Viszont nem biztos, hogy érdemes nyakló nélkül mindent ezeregy külön szálba nyomorítani, hisz ez számos hiba forrása lehet.

A szálak kialakítására nincs általános érvényű szabály, de a tapasztalat szerint, a futás során párhuzamosan végrehajtható programrészeket érdemes külön szálba rakni. További alkalmazási terület lehetnek azon esetek, amikor a program egy olyan feladatot hajt végre, ami valószínűleg várni kell: nyomtatás, perifériakezelés, hálózati hozzáférés. Ilyen esetekben a lassú tevékenység miatt nem férhetünk főprogramunkhoz, nem tudjuk egyszerű módon megszakítani a nem kívánatos lassú tevékenységet. Pl. a nyomtatás végrehajtása egy szövegszerkesztőben tipikusan másik szál feladata, hisz ez időt vesz igénybe, és a felhasználó szeretné munkáját folytatni a nyomtatás elindítása után, nem feltétlenül várja ki annak befejezését. További példa egy hálózati erőforrás elérése, amikor nem kívánjuk az egész folyamatot végigvárni, ráérünk a kapcsolat létrejöttekor azzal foglalkozni.

Hogy is van ez Delphi-ben

Delphi-ben új szál létrehozását a TThread osztály segítségével tehetjük meg. A TThread osztályból származtatjuk saját szálaink objektum típusait. A származtatott típusú szálak közül több is futhat, de természetszerűen lehet minden szálunk különböző típusú is.

TThread osztály

Metódusok:

  • constructor Create(CreateSuspended: Boolean);

  • Létrehozza a thread objektum utódát. Ha a paraméterben átadott CreateSuspended: Booleanváltozó értéke igaz, az Execute(futtató) függvény nem lesz mindaddig meghívva, míg nem hívjuk meg a Resumemetódust.
     
  • procedure DoTerminate; virtual;

  • A szál OnTerminate eseményét kezeli le, de nem ő semmisíti meg a szálat.
  • procedure Execute; virtual; abstract;

  • Az Executemetódusnak kell tartalmaznia a szál futtatandó kódját. A TThread osztálynál absztraktként definiált, így nekünk feltétlenül be kell illesztenünk a metódus kódját. Továbbá az Executemetódusnak kell figyelnie a Terminatetulajdonságot - melyet a Terminatemetódussal állíthatunk igaz értékre -, hogy be kell -e fejeznie futtatását.
     
  • procedure Resume;

  • A metódus hatására újraindítódik a felfüggesztett szál futása. A Resumemetódust annyiszor kell meghívni, amennyiszer a Susped-et hívtuk meg, mivel a Susped meghívások egymásba-ágyazhatóak.
  • procedure Suspend;

  • A metódus hatására felfüggesztődik a szál futása. A metódus többször is meghívható.
  • procedure Synchronize(Method: TThreadMethod);

  • Delphi-ben a szálak kezelésekor van egy olyan sajátosság, mely során csak a főszálban módosíthatjuk a VCL elemek metódusait és jellemzőit. Így az a faramuci helyzet áll elő, hogy külön rutint kell írni, mely az adott VCL komponenst kezeli, és ezt a metódust hívja meg a szál Synchronizemetódusa a főszál segítségével. Így elkerülhetjük a több-szálú konfliktusokat. A Synchronize metódus futtatása alatt a szál Suspend-elve van.
  • procedure Terminate;

  • A Terminate metódus segítségével állíthatjuk a Terminatejellemzőt ígaz (True) értékre. Ez a jellemzőt kell az Execute vagy más beágyazott metódusnak vizsgálnia a kilépéshez. (pl. repeat valami until not Terminate;)
  • function WaitFor: Integer;

  • A függvény mindaddig vár, míg a futtatás be nem fejeződik, ezután visszaadja a ReturnValuejellemző értékét (ez hasonló a Result érkékekhez).
Jellemzők:
  • property FreeOnTerminate: Boolean;

  • Megadja, hogy a szál objektumnak magától meg kell-e semmisülnie, miután a futtatás befejeződött (Terminated).
  • TThreadPriority =

  • (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical);
    property Priority: TThreadPriority;
    A szál prioritását állíthatjuk be, az alábbi prioritás értékek definiáltak:
tpIdle A szál csak akkor kerül futtatásra, ha a rendszer idle-Windwos állapotban van, nem szakítja meg más szálak futását.
tpLowest A szál prioritása két ponttal a normál alatt van.
tpLower A szál prioritása egy ponttal a normál alatt van.
tpNormal A szál alapértelmezett prioritáson van.
tpHigher A szál prioritása egy ponttal a normál felett van.
tpHighest A szál prioritása két ponttal a normál felett van.
tpTimeCritical A szál prioritása a lehető legmagasabb prioritást kapja, NT alatt realtime-nak nevezzük.
    Nem érdemes nagyon megnövelni egy adott szál prioritását, mert az a többi programunkban futó szál kárára válhat.
  • property ReturnValue: Integer;

  • Ez a tulajdonság visszatérési értéket ad vissza, segítségével a fő szál meggyőződhet arról, hogy sikeres volt a futtatása. (lásd WaitFor())
  • property Suspended: Boolean;

  • Ez a tulajdonság megadja, hogy a szál felfüggesztett állapotban van-e.
  • property Terminated: Boolean;

  • Ez a tulajdonság megmutatja, hogy a szál befejezési kérelmet kapott-e. (Amint fent említettem az Execute, illetve beágyazott metódusainak kell figyelnie a Terminate tulajdonságot, hogy be kell-e fejeznie futtatását.)
Mivel elég későre jár, és holnap leadás van zárom soraimat, következő cikkünkben a szálak gyakorlati részével foglalkozunk, és veszünk pár példát. Addig is ajánlom a Borland Delphi példáját a X:\Program Files\Borland\Delphi 3.0\Demos\Threads\ könyvtárban. Ez Kumm példájához hasonlóan látványos rendező algoritmusokkal mutatja be a szálak különálló futását. (A szálak különböző sebséggel végeznek, a rendező algoritmus hatékonysága szerint, s nincs semmi köze a beállított prioritáshoz, mindegyik normal prioritású.)

Melléklet:

A Borland Delphi Threads példája lefordítva: Thrddemo.exe
Cikkem meglehetősen rövid lett, mentségül csak azt hozhatom fel, hogy hasznosan töltöttem a cikkre nem fordított időt, Olvastam. JMéghozzá érdekes, izgalmas, és hasznos könyvet olvastam. Csak nagy ritkán tudok időt szakítani eme kellemes és hasznos elfoglaltságra, a könyvforgatásra széleskörű teendőim mellett. Az alábbi könyvet egyik osztálytársamnál (csoporttársamnál) láttam meg és beleolvasva érdekesnek tűnt. Burce E. Olson Bruchko című könyvéről van szó. A könyv vadregényes tájon és körülmények között játszódik, egy 19 éves fiatalember a gyilkos indiánok közé megy, hogy Életet és értelmet adjon életüknek (jó kis mondat). A könyv tartalma megtörtént esemény, a valóságot leíró dokumentumregény. Ajánlom figyelmetekbe, tényleg jó könyv, olvassátok el előítéletek nélkül!