Documente Academic
Documente Profesional
Documente Cultură
2. Excluderea mutuală care cere ca două părţi concurente ale unui program să nu fie
executate concurent. Aceste părţi se numesc secţiini critice, alt proces trebuie să fie
impiedicat să treacă la execuţia secţiunii sale critice până când primul nu iese din
execuţia secţiunii critice.
Se poate defini o clasă a secţiunii critice care constă dintr-un set de secţiuni critice
care trebuie să fie executate prin excludere mutuală cu alte secţiuni din aceeaşi clasă, fără
însă a fi necesară execuţia prin excludere mutuală cu alte secţiuni critice din alte clase.
3. Problema producător-consumator implică existenţa a două procese. Procesul
producător generează date pe care le transmite procesului consumator. Se cere ca:
Articolele de date să fie recepţionate de către consumator în aceeaşi ordine în care
au fost transmise de către producător.
Să nu piardă nici un articol de date prin transferul de la producător la consumator.
Să nu fie inserate alte articole (sau duplicate ale unora existente) în timpul
transferului.
4. Problema cititori şi scriitor implică existenţa unui fişier de date în care un proces
scriitor depune date. Iar procesele cititoare le preiau de acolo. Se cere :
Orice număr de procese cititoare să poată accesa simultan (fără excludere
mutuală) fişierul de date;
Dacă un proces scriitor a început să depună date în fişier, nici unui task cititor nu
trebuie să i se permită accesul în fişier.
2. Regiuni critice
C A. R. Hoare a propus o construcţie (specifică limbajelor de programate de nivel înalt)
with v do Q,
prin care se simplifică utilizarea semafoarelor, v reprezintă o variabilă declarată comună
var v : shared T;
T este tipul variabilei v, iar Q reprezintă o secvenţă critică de instrucţiuni. In acest mod,
regiunile critice la care se referă variabila v se vor exclude reciproc în timp. Utilizând
această construcţie se evită necesitatea încadrării secţiunii critice cu primitivele P şl V,
sau wait şi set (dacă se lucrează cu variabile eveniment).
Regiunile critice pot fi realizate şi cu mecanismele din nucleele sistemelor de
operare multitasking. Implementarea regiunilor critice este simplă. Declararea variabilei
v ca variabilă comună implică, de fapt, construirea unui semafor, sau a unei variabile de
tip eveniment.
O dezvoltare a construcţiei de mai sus s-a materializat prin regiunile critice condiţionate:
with v then B do Q:
unde B este o expresie logică. Execuţia secvenţei critice Q este condiţionată, în plus faţă
de cazul precedent, de faptul că B trebuie să aibă valoarea logică. ■adevărat (true). Dacă
după intrarea în regiunea critică, această condiţie nu este îndeplinită, atunci tascul este
trecut în aşteptare până la îndeplinirea ei.
Şi această construcţie poate fi realizată cu primitivele nucleului unui system de
operare multitasking. Pentru implementarea acestei construcţii se propune utilizarea
primitivelor P şi V. Fie semv, semaforul pe care se aşteaptă obţinerea accesului în
regiunea critica şi queue B, coada de asteptare în cazul în care nu este îndeplinită condiţia
B. Secvenţa (prezentată în limbaj de descriere):
P(semv);
wait(B);
*secventa critica;
V(semv);
set(B)
nu satisface totdeauna cerinţele din cauză că este posibilă situaţia în care condiţia B nu
este îndeplinită datorită activităţilor altor taskuri. Aceste taskuri, pentru a modifică
valoarea expresiei logice B, trebuie la rândul lor să intre în regiuni critice controlate de
aceeaşi valoare comună v. În acest caz taskurile se blochează reciproc.
Un exemplu de astfel de situaţie este cel al două sau mai multor taskuri care
schimbă între ele mesajul prin intermediul unei cutii postale. Taskurile trebuie să se
excludă reciproc la depunere, sau preluare de mesaje. Fie semn semaforul care
controlează accesul la cutia poştală (utilizând P(semv)). Un task nu poate prelua mesajul
dacă nu îndeplineşte condiţia semhold.val > 0. Atunci el blochează accesul altui task,
care vrea să depună un mesaj.
repeat
P(semv);
if non B then
begin
V(semvv);
*secventa de relaxare;
endthen;
until B;
reset(B);
*secventa critica;
set(B);
V(semv);
Mai sus s-a implementat o structură care execută o secvenţa de relaxare pentru verificarea
accesului la variabila logică B, asupra căreia s-au definit operaţiile set şi reset. Aceasta
poate fi, de exemplu, o temporizare realizată prin intermediul ceasului.
7.3. Monitoare
În implementarea regiunilor critice condiţionate apare dificultatea realizării primitivei
wait(B), C. A. R. Hoare si Brinch Hansen au propus un alt concept derivand din regiunile
critice condiţionate, numit monitor, şi constând dintr-o structură de date împreună cu
procedurile de acces la ea. Taskurile pot citi şi modifica datele, dar numai un singur task
poate utilize procedurile (sau, o procedura a) unui monitor la un moment dat.
Folosirea de către utilizator a unui monitor presupune secvenţa de forma:
sistemul de operare sau mediul de executie construieste cozi de aşteptare (vezi variabilele
de tip eveniment). Fiecare dintre proceduri va fi cuprinsă între primitivele wait şi set
astfel încât numai un task foloseşte la un moment dat o procedură.
Pentru exemplificare, se construieşte o cutie postală cu procedurile de acces la ea sub
forma unui monitor. Se defineşte tipul sequence of messages ca fiind o listă simplu
înlănţuită unde:
Dacă implementarea schemei de mai sus s-ar face astfel încât numai un singur task să
intre la un moment dat în monitor, atunci în ipoteza că trebuie să aştepte pentru a depune
un mesaj într-o cutie poştală plină. Soluţia de excludere mutuală de la utilizarea unei
singure proceduri evită anomalia menţionată.
Crearea firelor prin extinderea clasei Thread se face prin declaratii de forma
urmatoare :
public class NumeFir extends Thread {
public void run () {
………….
}
}
S-a definit un nou fir numit NumeFir care executa ceva la apelul metodei run() .
Metoda run() este asemenatoare ca functionalitate cu functia main() din limbajul C sau
C++ . Ea contine corpul principal al codului care va fi rulat de firul de executie.
Implementarea interfetei Runnable are ca efect realizarea unui fir de executie separat.
Firele trebuie instantiate in ambele cazuri .
Pentru crearea unei instante a clasei NumeFir trebuie adaugata declaratia :
Firele create trebuie lansate in executie . Aceasta se realizeaza pentru exemplul dat
cu :
nume.start();
Executia unui fir poate fi suspendata temporar cu metoda stop(), adica pentru
exemplul dat :
nume.stop();
nume.suspend();
nume.resume();
Metoda stop() nu distruge firele . Pentru a elimina complet un fir trebuie apelata
metoda destroy(). In Java nu este obligatorie distrugerea firelor. Sistemul Java de
colectare a gunoiului rezolva aceasta problema . Pentru a avea certitudinea ca au fost
sterse toate referintele la obiectul fir de executie de firul nume , se recomanda utilizarea
instructiunii :
nume = null;
Un exemplu de program care are pe langa firul principal de executie inca doua
(Thread-1 si Thread-2) este urmatorul :
***Aplicatie cu fire***
Firele au fost startate
Acesta este firul : Thread-1
Acesta este firul : Thread-2
}
}
public class InterfataRunnable_b inplements Runnable {
public void run() {
System.out.println(Fir de tip b cu indicatorul : +
Thread.currentThread().getName() );
}
}
Sincronizarea firelor
Un obiect sau o metoda pot fi accesate de mai multe fire de executie . Pentru a
realiza excluderea mutuala de la executarea unei sectiuni critice se poate folosi cuvantul
cheie synchronized().Ca exemplu de sincronizare este :
public void numeIndex() {
synchronized (index) {
index++;
……….
}
}
Metoda numeIndex contine un bloc sincronizat . Obiectul index este blocat de un fir
care a inceput utilizarea metodei pana la terminarea executiei blocului respectiv .
Tratarea exceptiilor
Dupa cum s-a vazut si din exemplele anterioare , limbajul Java ca de altfel si
limbajul Ada ofera posibilitati de tratare a exceptiilor . Exceptiile sunt erori remediabile .
Tratarea exceptiilor face programele mai robuste si mai performante. Aceasta este o
cerinta foarte importanta pentru aplicatiile de timp-real .
In Java , exceptiile sunt considerate o clasa de obiecte care pot trata diferite
probleme aparute in executie .
Exista trei moduri pentru tratare a exceptiilor :
ignorarea lor ;
prelucrarea lor de catre codul in care apar ;
semnalizarea lor codului care a apelat metoda ce a generat exceptia pentru a
fi prelucrata de catre aceasta .
Exceptiile care nu sunt tratate explicit de catre program sunt transmise interpretorului
Java care le trateaza sau opreste executia programului .
Exista mai multe metode oferite de limbajul Java pentru tratarea exceptiilor :
1. catch care dupa cum spune si numele ei (interceptare) cuprinde codul care
detecteaza aparitia unei exceptii si o trateaza in mod corespunzator.
2. try specifica faptul ca programul va incerca sa execute un bloc de cod care
genera o exceptie .
3. finally precizeaza actiunile care urmeaza a fi executate in cazul in care nici una
dintre instructiunile catch anterioare nu a rezolvat problema .
Planificarea firelor
Exista mai multe moduri de executie a firelor unui program scris in Java . Unul este
prin divizarea timpului ( engl. : timeslicing ) si consta din alocarea periodica a unor mici
cuante de timp fiecarui fir .Celalalt mod implica planificarea firelor . Variante mai vechi
ofereau numai planificarea nepreemtiva . Tendinta de evolutie a mediilor de excutie Java
este de includere a facilitatilor necesare unor aplicatii de timp-real . Acestea ar trebui , in
primul rand , sa poata specifica explicit obiecte rezidente permanent in memoria interna
( operativa ) a sistemului si sa aplice planificarea preemptiva .
Cand intr-un calculator exista mai multe procese care trebuie si pot fi executate in
acelasi timp , se spune ca ele sunt executate concurent . Dupa cum s-a mentionat , daca
sistemul de calcul este construit cu un singur procesor , executia concurenta a proceselor
se realizeaza prin intretesere . In cazul unui sistem de calcul multiprocesor, problema
intreteserii se pune numai atunci cand numarul proceselor care trebuie executate la un
moment dat depaseste pe cel al procesoarelor . Sistemele multiprocesor ofera posibilitatea
prelucrarii paralele, in timp ce sistmele monoprocesor o ofera numai pe cea a unei
prelucrari virtual paralele .
Indeplinirea activitatilor pe care trebuie sa le realizeze ansamblul de taskuri pentru
implementarea algoritmilor de control, implica cooperarea lor .
Aceasta consta din :
sincronizarea dintre taskuri ;
sincronizarea cu timpul-real ;
excluderea mutuala de la utilizarea resurselor ;
comunicarea dintre taskuri.
Este dificil de realizat o diferenţiere completă a acestor activităţi. De exemplu,
efectuând o operaţie de comunicare prin rendezvous, două taskuri realizează implicit o
sincronizare intre ele.
Sincronizarea cu timpul-real
În capitolul s-au prezentat rutinele tdelay, twake şi wait. Cea de-a doua rutină
serveşte la reactivarea unui task, la un moment precizat. Pentru execuţia periodică (cu
perioada deltat) a unei activităţi se presupune secvenţa:
repeat
today(deltat : tine);
*secvenţa de instrucţiuni a activităţii:
wait;
forever;
repeat
today(deltat : tine);
wait;
*secvenţa de instrucţiuni a activităţii:
forever;
repeat
receive (cutie, mes, deltat);
*secvenţa de instrucţiuni:
forever
task 1 is;
var mes : message;
………….
repeat
recive(cutie1,mes,deltat);
recive(cutie3,mes);
send(cutie3);
forever
task 2 is;
................
repeat
recive(cutie2,mes);
send(curie3,mes);
*secventa de instructiuni;
forever
task 1 is;
begin
repeat
.........................
*evenimentul A;
*continuă dacă a avut loc evenimentul B;
.........................
forever
end task 1;
task 2 is;
begin
repeat
........................
*evenimentul B;
*continuă dacă a avut loc evenimentul A;
.......................
forever
end task 2;
se sincronizează reciproc. Fiecare trebuie să-l aştepte pe celălalt, dacă acesta nu a ajuns în
punctul de sincronizare.
Dacă în task 1 ar lipsi *continuă dacă a avut loc evenimentul B; atunci
s-ar obţine o sincronizare unilaterală. În acest caz, task 2 aşteaptă ca task 1 să efectueze
”evenimentul A”, şi chiar dacă nu a avut loc evenimentul B, task 1 îşi
va continua activitatea.
Sincronizarea reciprocă:
*secvenţe de iniţalizare:
var queue1, queue2 : ^tcb;
begin
init (queue1);
init (queue2);
....................
task 1 is;
begin
repeat
......................
continue (queue2); t2
delay (queue1);
......................
forever end task 1;
task 2 is;
begin
repeat
......................
continue (queue1); t1
delay (queue2);
.....................
forever
end task 2;
2. Sincronizarea cu primitivele P şi V
Exemplul 2:
Sincronizare reciprocă
*secvenţa de iniţializare:
var sen1, sem2 : semaphore;
begin
........................
init (sem1.next);
init (sem2.next);
sem1.val : =0;
sem2.val : =0;
........................
Un semafor sp este propriu sau privat pentru un anumit task, dacă numai el
poate executa operatia P ( sp ). Celelalte taskuri pot executa numai operaţia V
( sp ).
Exemplul 3:
Sincronizarea a n taskuri
*secvenţa de iniţializare:
var sp,sm,se : semaphore;
b1,b2,...,bn : boolean;
end
begin
b1 :=true;
b2 :=false;
......................
bn := false;
init(sp.next);
init(sm.next);
init(se.next);
sm.val :=1
sp.val :=0
se.val :=0
....................
task1 is;
...................
P(sm);
If b1 and b2 and ... and bn thenV (sp);
else b1 :=false;
V(sm);
P(sp);
for 1 :=2 to n do V (sp);
...................
task i is;
......................
P (sm);
b1 :=true
if b1 and ... and bn then V(sp);
V(sm);
P(se);
..................
Secventele cuprinse intre P (sm) sunt sectiuni critice, si se execută prin excludere
mutuală.
Sincronizare recipocă
*segvenţa de iniţializare:
Var ev1,ev2 : event;
begin
init(ev1.next);
init(ev2.next);
reset(ev1);
reset(ev2);
................
Se pot utiliza pentru sincronizarea două cutii postale care sunt initial goale.
Un task va aştepta incercând sa citeasca un mesaj dintr-o cutie postală goala
şi-l va scoate pe altul din asteptare, depunand un mesaj in cutia lui postala.
Exemplul 5:
Sincronizare reciproca
*secventa de initializare:
var v:shared T;
b,b:boolean;
begin
b:=false;
b:=true;
............
Este evidenta sincronizarea celor doua task-uri. In mod similar se poate realiza
sincronizarea mai multor task-uri.
In exemplul precedent, continuturile procedurilor nu au importanta pentru
sincronizare.
Un alt mod de realizare a sincronizari este acel care utilizarea conceputul de
obiect si se specifica utilizarea lui specifica.
O resursa se numeste proprie, daca ea este utilizata numai de un singur task pe tot
parcursul activitatii ansamblului de taskuri. Ea se numeste comuna sau partajabila, daca
este urilizata de cel putin doua taskuri. O resursa se numeste partajabila cu n puncte de
acces (n>1), daca poate fi utilazata simultan de n taskuri. Ea se numeste resursa critica,
daca este comuna si are un singur punct de acces, adica, la un moment dat poate fi
utilizata numai de un singur task desi exista mai multe solicitari.
Mai multe taskuri care utilizeaza simultan, in comun, o resursa cu mai multe
puncte de acces, se numesc paralele din punctul de vedere al resursei. Daca mai multe
taskuri utilizeaza o resursa critica, atunci ele trebuie sa se excluda mutua l(sau reciproc,
in timp) de la folosirea ei. Secventa de instructini dintr-un task in care se utilizeaza
resursa critica se numeste sectiune critica.
O sectiune critica trebuie sa fie cat mai scurta posibil, astfel incat sa nu intarzie
prea mult celelalte taskuri(unele avand poate o prioritate mai mare). Prima varianta de
excludere reciproca a fost realizata hard, prin generarea unui semnal cand un task intra
intr-o sectiune critica. Acest smnal inhiba sistemul de intreruperi, astfel fiind impiedicate
alte taskuri sa intre in executia. Dezavantajul acestei metode consta din dificultate
realizari unor inhibari selective si dinamice.
Exemplul 7 :
var ias: boolean; {indicator de acces la resurse}
queue: ^tcb;
end
*secventa de initializare:
ias:=true;
init (queue);
Inconvenientul acestei metode consta in faptul ca, daca un task a asteptat sa obtina
accesul la sectiunea sa critica si a fost reactivat, se poate intampla ca un alt task cu
prioritate mai mare ca a sa, sa ocupe resursa. Prin urmare, se poate ajunge la situatia in
care resursa sa fie utilizata in comun.
Anomalia nu apare daca exista siguranta ca accesul la variabila ias este invizibila,
mai precis ca primele doua linii ale secventei precedente sunt indivizibile.