Bevezetés
A párhuzamosság két fajtája a PEARL programozási nyelvben:
- Aszinkron folyamatok (TASK-ok), melyek egymástól függetlenül, párhuzamosan futnak, és egy esemény hatására,
vagy konkrét időpontban indulnak
- Folyamatok szinkronizációját lehetővé tevő komponensek
Taszkok működésének háttere:
Amennyiben 1 processzor elérhető, a taszkoknak versenyeznie kell a használatáért, de más erősforrásokért is,
mint például I/O eszközök. Az operációs rendszer a taszk prioritása alapján rendel hozzá erőforrást.
Ez a prioritás egy pozitív egész szám, mely minnél kisebb annál magasabb. Ha egy taszk lefoglalja az egyetlen processzort, és
benyújtja az igényét egy másik erősforrásra, akkor az operációs rendszer elveszi a taszktól
a processzort és odaadja
a rá váró legmagasabb prioritású futtatható taszknak. Ha több ugyanolyan prioritás is előfordul, akkor a Round-Robin-
Stratégiát alkalmazza a processzor.
Taszk deklaráció és specifikáció
Taszkot csak modul szinten lehet deklarálni, ezért csak olyan adatokat változtathatunk meg taszkokkal, amelyeket a
modul szinten deklaráltunk, és paraméterek nincsenek engedélyezve.
Taszk deklaráció általános formája:
TaskDeclaration ::=
Identifier: TASK [ PriorityAttribute ] [ MAIN ] [ GlobalAttribute ] ;
TaskBody
END;
PriorityAttribute ::= { PRIORITY | PRIO } IntegerWithoutPrecision§GreaterZero
TaskBody ::= [ Declaration... ] [ Statement... ]
Ha nem adunk meg prioitást, akkor 255 lesz hozzárendelve.
Taszk deklaráció általánosan:
TaskSpecification ::=
{ SPECIFY | SPC } Identifier§Task TASK GlobalAttribute;
GlobalAttribute ::= GLOBAL [ (Identifier§OfaModule) ]
A MAIN kulcsszóval jelölt taszkok a program kezdetekor elindulnak a prioritásuknak megfelelő sorrendben.
A taszkkezelés a taszk objektum referenciáin keresztül is végbemehet. Deklaráció:
TaskReferenceDeclaration ::=
{ DECLARE | DCL } Identifier or IdentifierList [ DimensionAttribute ]
[ AllocationProtection ] REF TASK [ GlobalAttribute ] [ InitialisationAttribute ];
Specifikáció:
TaskReferenceSpecification ::=
{ SPECIFY | SPC } Identifier or IdentifierList [ VirtualDimensionList ]
[ AllocationProtection ] REF TASK GlobalAttribute;
Kétféleképp lehet lefoglalni taszknak memóriacímet: a TASK kulcsszóval, vagy úgy, hogy hozzárendelünk egy TASK változóhoz
egy task referencia változót. A két módszer lényegében ekvivalens.
Pl:
SPC TemperatureMeasurement TASK GLOBAL;
DCL PtrTask REF TASK;
...
PtrTask := TASK (TemperatureMeasurement);
PtrTask := TemperatureMeasurement;
A referencia taszkok azonosítására is alkalmas az IS illetve az ISNT operátorok használatával.Pl:
IF PtrTask IS TASK(hello) THEN
...
FIN;
Taszk prioritását lekérdezhetjük a PRIO beépített függvénnyel:
DCL CurrTaskA FIXED(15);
SPC TaskA TASK GLOBAL;
...
PrioTaskA := PRIO (TaskA);
Taszkok kezelése
A taszk futásának feltételei:
Szintaxis: Task Start ::= [ StartCondition ] ACTIVATE Name§Task;
Ha a StartCondition igaz, a taszk futtathatóvá válik, de még nem indul el csak akkor ha ez lesz a
legmagasabb prioritású futtatható taszk.
StartCondition ::=
AT Expression§Time [ Frequency ]
| AFTER Expression§Duration [ Frequency ]
| WHEN Name§Interrupt [ AFTER Expression§Duration ] [ Frequency ]
| Frequency
Frequency ::= ALL Expression§Duration [ { UNTIL Expression§Time } | { DURING Expression§Duration } ]
Példák:
AT 20:0:0 ACTIVATE Statistics; //8 órakor induljon el
AFTER 20 ACTIVATE Statistics; //20 másodperc múlva induljon el
WHEN Alarm ACTIVATE ShutDown; //amikor az Alarm megszakítás (esemény) bekövetkezik akkor induljon el
ALL 2 HRS ACTIVATE Protocol; //két óránként induljon el
Taszk elindítása:
TaskStart ::= [ StartCondition ] ACTIVATE Name§Task [ Priority ];
Priority ::= { PRIORITY | PRIO } Expression§WithPositiveIntegerAsValue
Leállítás:
TaskTermination ::= TERMINATE [ Name§Task ] ;
Halasztás:
TaskSuspension ::= SUSPEND [ Name§Task ] ;
Folytatás (egy halasztott taszkra vonatkozik):
TaskContinuation ::= [ SimpleStartCondition ] CONTINUE [ Name§Task ] [ Priority ]
SimpleStartCondition ::= AT Expression§Time | AFTER Expression§Duration | WHEN Name§Interrupt
Késleltetés:
TaskDelay ::= SimpleStartCondition RESUME;
Példa: A Control taszk bekapcsol egy eszközt, aztán 10 másodperc múlva leellenőrzi hogy működik-e :
Control: TASK;
! turning on the device
AFTER 10 SEC RESUME;
! ckecking the function of the device
...
END; ! Control
Elérése annak hogy a betervezett taszkindulás mégse történjen meg:
TaskPrevent ::= PREVENT [ Name§Task ] ;
Taszkok szinkronizálása
Motiváció, példafeladat:
Két taszk, Producer1, Producer2 üzeneteket generálnak, és egy közös bufferben tárolják. Egy Output nevű
taszk pedig kiírja a képernyőre a bufferbe került üzeneteket. A helyes együttműködés érdekében:
- ha a buffer üres, akkor a kimenetre nem kerül üzenet
- addig nem kerül a bufferbe üzenet, amíg az Output ki nem írta a képernyőre az előzőt
- Producer1, és Producer2 köcsönösen ki kell zárják egymást
Ezen célok eléréséhez kétfajta szinkronizációs változót használhatnuk, semaphore-t és bolt változót.
Szemafor változók
Deklarálás:
DCL Identifier or IdentifierList SEMA;
Példa:
DCL (On, Off) SEMA;
Deklaráció után "locked" állapotban van.
Ha azt akarjuk hogy "locked" állapotba kerüljön, akkor:
REQUEST Name§Sema;
Ennek hatására:
- Ha az értéke nagyobb 0-nál, akkor csökken 1-gyel
- Ha az értéke 0, akkor marad is 0, és a végrehajtó taszk bekerül a szemafor változóhoz tartozó sorba, szóval
a taszk blokkolva lesz.
Szemafor felengedés:
RELEASE Name§Sema;
A szemafor változó értéke 1-gyel megnő, és a szemafor sorában levő taszkok felengednek (nem lesznek blokkolva).
Ezekkel az eszközökkel a bufferelési probléma megoldása:
PROBLEM;
DCL (Into buffer, Out of buffer) SEMA;
Consumer: TASK;
RELEASE Into buffer;
ACTIVATE Producer 1;
ACTIVATE Producer 2;
REPEAT
REQUEST Out of buffer;
! Output to the protocol printer
RELEASE Into buffer;
END; ! Loop
END; ! Consumer
Producer 1: TASK;
REPEAT
! Preparing the message
REQUEST Into buffer;
! Buffering
RELEASE Out of buffer;
END; ! Loop
END; ! Producer 1
Producer 2: TASK;
REPEAT
! Preparing the message
REQUEST Into buffer;
! Buffering
RELEASE Out of buffer;
END; ! Loop
END; ! Producer 2
A TRY kulcsszóval is lehet kezelni szemafort:
TRY Name§Sema;
Ha a szemafor "free" állapotban van, akkor végrehajt rá egy REQUEST-et és visszaad egy 1-est. Ha azonban "locked"
állapotban van akkor nem csinál semmit vele, és 0-t ad vissza.
Holtpont alakulhat ki a következő esetben:
Task T1
REQUEST S1;
1st segment
REQUEST S2;
2nd segment;
RELEASE S2;
RELEASE S1;
Task T2
REQUEST S2;
1st segment
REQUEST S1;
2nd segment
RELEASE S1;
RELEASE S2;
Ilyenkor S1 és S2 egymásra vár a végtelenségig. Ezt a helyzetet elkerülhetjük a következő módon:
REQUEST Name§Sema [ , Name§Sema ]... ;
Itt több szemafort sorolunk fel. Ekkor a futó taszkot elhalasztjuk, ha a felsorolt szemaforok közül legalább 1
"locked" állapotban van. A taszk nem folytatódik, amíg az összes szemafor nem kerül "free" állapotba.
RELEASE után is felsorolhatunk szemaforokat, de ennek hatása megegyezik azzal, mintha egyesével hajtanánk végre
a RELEASE-t rajtuk.
Ezek után a holtpontmentes megoldás a következő:
Task T1
REQUEST S1, S2;
1st segment
2nd segment
RELEASE S1, S2;
Task T2
REQUEST S1, S2;
1st segment
2nd segment
RELEASE S1, S2;
A szemaforok sorrendje a felsorolásban nem számít, továbbá a TRY operátor után csak 1 szemafor állhat.
A szemafor változók is modul szinten deklarálhatók, és inicializálhatók a PRESET kulcsszóval:
DCL (S1, S2) SEMA PRESET(3, 5);
Bolt változók
Arra valók, hogy ha több folyamat szeretne használni, illetve módosítani egy adatot egymást kölcsönösen kizárva.
Egy bolt változónak 3 állapota:
- "locked": az erőforrást használja valaki
- "lock possible": az erőforrás szabad
- "lock not possible": az erőforrást szimultán módon használják
Deklaráció:
DCL Identifier or IdentifierList BOLT;
Deklaráció után "lock possible" állapotba kerül.Legyen B egy bolt változó, ami egy erőforráshoz van hozzárendelve.
RESERVE B jelentése: belépünk a kritikus szakaszba, ahol az erőforrást használjuk, és ekkor a többi taszk hozzáférése lockolva lesz.
A FREE B; utasítással lépünk ki a kritikus szakaszból.
Hatások:
- RESERVE B : ha B "lock possible", akkor "locked" lesz; különbena végrehajtó taszkot elhalasztjuk, és betesszük a B-hez
hozzárendelt sorba
- FREE B : B "lock possible" lesz, majd minden B sorában várakozó taszk feléled, és prioritás szerinti sorrendben
végrehajtódik. Ha RESERVE miatt egy taszk sem várakozik, akkor az ENTER miatt várakozók ébrednek fel, és hajtódnak
végre prioritás szerinti sorrendben.
- ENTER B : Ha B "locked", vagy a B sorában van olyan taszk, ami RESERVE miatt várakozik, akkor a végrehajtó taszk szünetel
és bekerül B sorába. Különben B "lock not possible" állapotba kerül, hogy megtiltsa a kizárólagos elérést. Ezen
felül növelünk egy Z változót, ami jelöli a B-t használó taszkok számát.
- LEAVE B : ha Z=1, akkor ugyanaz a hatás mint FREE B.nél, különben Z-t csökkentjük 1-gyel, és B marad "lock not possible"