A PEARL programozási nyelv

Párhuzamosság

Bevezetés

A párhuzamosság két fajtája a PEARL programozási nyelvben:

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:

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:

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:

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: