Sunteți pe pagina 1din 8

Semafoare

Declararea unei variabile de tip semafor:


var s:semaphore;

Valori posibile: numere naturale.

Operaţii permise:
1) initial(s,s0)
2) wait(s) :
if s>0
then ss-1
else procesul curent este blocat
3) signal(s) :
if există procese blocate
then unul dintre ele este deblocat
else ss+1

Operaţiile wait şi signal asupra aceluiaşi semafor se execută prin excludere


reciprocă.

Pentru un semafor s oarecare, următoarele relaţii sunt invariante:


s0 (1)
s=s0+#(signal)-#(wait) (2)
unde s0 este valoarea iniţială a semaforului, #(wait) indică de câte ori s-a trecut
complet de semafor (nu se numără deci procesele temporar blocate la semafor), iar
#(signal) este numărul operaţiilor signal executate. Relaţiile (1) şi (2) sunt
invariante în sensul că sunt îndeplinite la orice moment de timp.
Să verificăm de exemplu relaţia (2). Ea este satisfăcută la iniţializarea
semaforului. Fie un moment de timp oarecare la care relaţia este îndeplinită şi să
considerăm prima operaţie ulterioară asupra semaforului. Distingem cazurile:
- se execută o operaţie wait: dacă s=0 atunci cantităţile ce intervin în relaţie rămân
neschimbate; dacă s>0 atunci s scade cu o unitate, iar #(wait) creşte cu o unitate;
- se execută o operaţie signal: dacă există procese blocate de semaforul s atunci
#(signal) şi #(wait) cresc cu o unitate, valoarea lui s rămânând astfel neschimbată;
în caz contrar, #(signal) şi s cresc cu o unitate.

Excluderea reciprocă

Pentru realizarea excluderii reciproce, vom plasa secţiunea critică apare între
"parantezele" wait şi signal:

wait(s);
SC
signal(s);
SNC

Semaforul s trebuie iniţializat cu 1: dacă ar fi iniţializat cu 0, în momentul în


care oricare dintre procese ar încerca să intre în secţiunea sa critică, el ar fi suspendat la
executarea primitivei wait(s), contrazicându-se astfel condiţia de competiţie
constructivă; dacă ar fi iniţializat cu o valoare mai mare decât 1, atunci două procese
care doresc amândouă să intre în secţiunea lor critică vor reuşi acest lucru,
contrazicându-se condiţia de excludere reciprocă.
În continuare vom considera numai momentele de timp când toate procesele
sunt, într-o distribuţie oarecare, în secţiunile lor critice, necritice sau sunt blocate (deci
nici unul dintre procese nu este în curs de executare a primitivelor wait şi signal); de
exemplu după ce un proces a executat instrucţiunea signal, el este considerat deja ca
fiind în secţiunea necritică şi nu urmând să intre în aceasta.
Pentru a demonstra corectitudinea algoritmului, vom arăta că:
s+#(SC)=1 (3)
este o relaţie invariantă, unde #(SC) este numărul de procese ce se află în secţiunea lor
critică, ţinând cont de relaţia invariantă (2) şi de faptul că s0=1, este suficient să
demonstrăm că #(SC)=#(wait)-#(signal).
Această ultimă relaţie este iniţial adevărată şi rămâne adevărată atât la intrarea
oricărui proces în secţiunea critică (se măresc concomitent #(SC) şi #(wait)), cât şi
la intrarea oricărui proces în secţiunea sa necritică (se micşorează #(SC) şi se măreşte
#(signal)).
Din relaţia (3) rezultă că este îndeplinită condiţia de excludere reciprocă
(#(SC)1), precum şi cea de competiţie constructivă (dacă nici un proces nu este în
secţiunea critică, atunci s=1 şi deci unul dintre ele va intra sigur). Condiţia de
conexiune liberă este şi ea evident satisfăcută.

Semafoare în Java
class Semafor {
private int value = 0;
public Semafor(int initial) { value = initial; }
public synchronized void W() {
value--;
if (value < 0)
try { wait(); }
catch(InterruptedException e) { }
}
public synchronized void S() {
value++;
if (value <=0 ) notify();
}
}

Observaţii:
- constructorul clasei realizează, odată cu crearea unui obiect, şi iniţializarea
valorii value a semaforului;
- metodele W şi S corespund primitivelor wait şi signal descrise mai sus, ce
operează asupra semafoarelor;
- fie sem un semafor, deci un obiect de tipul Semafor. Atunci operaţiile
wait(sem) şi signal(sem) din modelul general propus de Dijkstra vor trebui
înlocuite respectiv prin sem.W() şi sem.S().
Schiţă de demonstraţie:
1) Există procese blocate dacă şi numai dacă valoarea lui value este negativă.
În acest caz, numărul proceselor blocate este -value;
2) prin notify() firul curent îşi continuă executarea. Firul trezit (dacă
există) devine gata de executare, dar datorită modificatorului synchronized nu
poate fi preluat de către un procesor decât când firul ce a executat notify()
părăseşte metoda sincronizată.

Exemplu. Problema grădinilor ornamentale.

class Tip extends Thread {


static int total; int i;
static Semafor ExRec = new Semafor(1);
Tip(int ii) { i = ii; }
public void run() {
int temp;
for (int j=0; j<10; j++) {
try { sleep( (int) (100 * Math.random()) ) ; }
catch (InterruptedException e) { }
ExRec.W();
IO.write(i+"\t");
temp = total; temp++; total = temp;
ExRec.S();
}
}
}
class Grad_Sem {
public static void main(String[] string) {
Tip[] Ob = new Tip[5];
for (int i=0; i<5; i++) Ob[i] = new Tip(i);
for (int i=0; i<5; i++) Ob[i].start();
try { for (int i=0; i<5; i++) Ob[i].join(); }
catch (InterruptedException e) { }
IO.writeln("\nTotal= " + Tip.total);
}
}

Jocul vieţii

Un tabel dreptunghiular a cu m linii şi n coloane conţine celule care pot fi vii


sau moarte (1 sau 0). Fiind dată o configuraţie a tabelului, se cere să se calculeze
configuraţia "generaţiei" următoare, ştiind că celulele moarte îşi păstrează starea, iar o
celulă vie moare dacă şi numai dacă numărul celulelor vecine vii este mai mic decât 2
sau mai mare decât 4. O celulă are cel mult 8 vecini: pe orizontală, pe verticală şi pe
diagonale. pentru un calcul facil al numărului vecinilor vom borda matricea cu zerouri.

Vom asocia fiecărei celule un proces în care apare o variabilă locală k. O


primă acţiune constă în a calcula (evident numai pentru celulele vii) numărul k al
celulelor vecine vii. O a doua acţiune constă în actualizarea stării fiecărei celule pe baza
valorii k calculate. Este evident însă că dacă actualizarea unei stări se face înainte ca o
stare vecină să îşi fi calculat valoarea k, atunci este posibil ca rezultatul final să fie
denaturat.
Jocul vieţii evidenţiează următorul tip de sincronizare: mai multe procese ce se
execută concurent trebuie să se aştepte unul pe altul până când toate îşi încheie o
anumită activitate. Trebuie deci realizat efectul metodei join din Java.
Celulele îşi vor desfăşura activitatea concurent cu procesul Control.
Sunt folosite semafoarele s (pentru excludere reciprocă, deci iniţializat cu 1) şi
sem (care joacă rolul unei bariere, deci iniţializat cu 0). Variabila globală nr numără
câte celule şi-au calculat vecinii.

Procesul Celula(i,j)
if a[i,j]=1
then k  a[i,j+1]+a[i-1,j+1]+a[i-1,j]+a[i-1,j-1]+
a[i,j-1]+a[i+1,j-1]+a[i+1,j]+a[i+1,j+1];
wait(s); nr  nr+1; signal(s);
wait(sem);
if a[i,j]=1
then if (k<2) or (k>4)
then a[i,j]  0

Procesul Control
while nr<m*n do ;
for i:=1 to m*n do
signal(sem)

Observaţii:
- sunt introduse doar două semafoare, indiferent de dimensiunile tabelului de celule;
- semaforul sem are în permanenţă valoarea 0 (joacă rolul unei bariere).

Problema Producător - Consumator

Fie lung lungimea benzii. Vom folosi semafoarele libere şi ocupate, a


căror valoare corespunde numărului de celule libere, respectiv ocupate de pe bandă;
corespunzător, ele vor fi iniţializate cu lung (lungimea benzii), respectiv 0.
Să observăm că relaţia libere+ocupate=lung nu permite folosirea unui
singur semafor!
Procesul Prod
for ch:='a' to 'z' do
wait(libere);
wait(ExRec);
pune(ch); write('P',ch,' ');
signal(ExRec);
signal(ocupate)
end

Procesul Cons
repeat
wait(ocupate);
wait(ExRec);
ia(ch); write('C',ch,' ');
signal(ExRec);
signal(libere)
until ch='z'

Problema filozofilor chinezi

Fiecărui filozof i îi asociem un proces Fi.


Apar două probleme: excluderea reciprocă asupra beţişoarelor şi evitarea
blocării totale.
Pentru rezolvarea excluderii reciproce vom asocia fiecărui beţişor i un semafor
binar bi, iniţializat cu 1; încercarea de ridicare a unui beţişor corespunde interogării
semaforului asociat beţişorului.
Pentru evitarea blocării totale introducem semaforul AccesLiber, care va
memora în permanenţă numărul de filozofi cărora le dăm voie să încerce să mănânce.
Iniţializarea sa se poate face cu orice valoare întreagă din intervalul [1,nf-1], dar
pentru maximum de libertate vom alege valoarea nf-1.
Pentru excluderea reciprocă asupra operaţiilor de scriere, vom mai folosi un
semafor binar s, iniţializat cu 1.

Filozoful Fi
repeat
filozoful i gândeşte
wait(AccesLiber);
wait(b[i]);
wait(b[(i mod nf)+1]);
wait(s); write('M',i:1,' '); signal(s);
filozoful i mănâncă
wait(s); write('G',i:1,' '); signal(s);
signal(b[i]);
signal(b[(i mod nf)+1]);
signal(AccesLiber)
until false
Este evident că mesajul de început al activităţii de a mânca trebuie inserat
imediat după ce se trece de semafoarele care controlează posibilitatea de a fi ridicate
beţişoarele din stânga şi dreapta filozofului.
Ceva mai puţin evident este că mesajul de început al activităţii de a gândi
trebuie inserat imediat înainte (şi nu după) semnalările repunerii beţişoarelor pe masă.
Dacă mutăm acest mesaj după operaţiile signal ce corespund repunerii beţişoarelor
pe masă, lucrurile se vor desfăşura correct din punctual de vedere al cerinţelor
problemei, dar incorrect din punctul de vedere al semalării acţiunilor. Astfel este
posibil să apară pe ecran secvenţa:
G4 G3 G1 G5 G2 M1 M2 G1 ...
deoarece mesajul "G1 " apare după ce filozoful 1 repune beţişoarele pe masă, între
timp filozoful 2 începând să mănânce.

Problema Cititori - Scriitori

Se consideră o carte la care au acces mai mulţi cititori şi mai mulţi scriitori.
Este permis ca mai mulţi cititori să aibă acces simultan la conţinutul cărţii, dar dacă un
scriitor operează asupra cărţii (completează, şterge, modifică etc.) atunci nici un alt
scriitor şi nici un alt cititor nu au acces la carte. Mai precis:
1) un cititor poate începe operaţia de citire dacă şi numai dacă nici un scriitor nu este
în curs de a scrie în carte;
2) un scriitor poate începe operaţia de scriere dacă şi numai dacă nici un cititor şi nici
un alt scriitor nu au acces la carte.

Încercările de deschidere a cărţii au şanse de reuşită doar dacă nici un scriitor


nu este activ. În acest caz ele se împart în două categorii:
a) cele de citire când există cel puţin un cititor activ;
b) cele de citire când nu există cititori activi + cele de scriere.
Încercările din prima categorie trebuie satisfăcute necondiţionat.
Încercările de deschidere a cărţii din a doua categorie fiind egal rivale, vom
controla executarea lor printr-un semafor sem ce trebuie iniţializat cu 1, deoarece orice
primă încercare de acest tip trebuie validată. Din motive de simetrie, sem va fi
actualizat (prin operaţia signal) în exact aceleaşi situaţii. Vom urmări ca semaforul
sem să aibă valoarea 0 dacă şi numai dacă la carte are acces cel puţin o persoană.
Semafoarele ExRec şi s sunt folosite pentru excludere reciprocă şi deci trebuie
iniţializate cu 1. Variabila nrcit ţine evidenţa numărului de cititori activi.

Procesele Cititori :
repeat
deschide(i,cit); preia informaţia dorită
inchide(i,cit); prelucrează informaţia citită
until false

Procesele Scriitori :
repeat
redactează un text nou; deschide(i,scr);
introdu textul in carte; inchide(i,scr);
until false

procedure deschide(i,caz)
if caz=citire
then wait(ExRec);
if nrcit=0 then wait(sem);
wait(s); write('C',i:1,'( '); signal(s);
nrcit++;
signal(ExRec);
else wait(sem);
wait(s); write('S',i:1,'( '); signal(s);
end

procedure inchide(i:integer; caz:acces)


if caz=citire
then wait(ExRec);
wait(s); write('C',i:1,') '); signal(s);
nrcit--;
if nrcit=0 then signal(sem);
signal(ExRec);
else wait(s); write('S',i:1,') '); signal(s);
signal(sem)
end

Acţiunile de deschidere şi închidere pentru citire trebuie executate sub


excludere reciprocă. Într-adevăr, la apelul inchide(i,cit) cu nrcit=1, după
decrementarea lui nrcit nu este sigur că nrcit=0, deoarece este posibil ca între timp
valoarea lui nrcit să se fi modificat printr-un apel deschide(j,cit). De aceea am
folosit semaforul ExRec, iniţializat cu 1.
Observaţie. Dacă am include "pentru siguranţă" şi operaţiile de deschidere şi
închidere pentru scriere între wait(ExRec) şi signal(ExRec), se poate ajunge la
blocare globală în următoarea situaţie:
- scriitorul 1 deschide cartea; sem şi ExRec au acum valorile 0, respectiv 1;
- cititorul 1 trece de semaforul ExRec şi se blochează la sem; acum ExRec=0;
- în continuare, orice alt proces se va bloca la ExRec.
În cele ce urmează vom înţelege prin stare a programului tripletul
(nrscr,nrcit;sem), unde nrscr este numărul de scriitori activi. Vom arăta că s
ingurele stări posibile sunt următoarele:
(0,0;1), (1,0;0) şi (0,n;0) cu n>0
starea iniţială fiind (0,0;1).
Dacă suntem în starea (0,0;1):
- poate avea loc o deschidere pentru citire sau scriere, ajungându-se în (0,1;0) sau
(1,0;0);
- nu poate avea loc o închidere, deoarece forma proceselor presupune că o închidere
este precedată de o deschidere, în contradicţie cu nrscr=nrcit=0.
Dacă suntem în starea (1,0;0), atunci:
- poate avea loc o închidere pentru scriere şi se trece în starea (0,0;1) (dacă nu
există procese blocate la semaforul sem) sau în una din stările (1,0;0), (0,1;0)
(dacă există procese blocate la semaforul sem);
- nu poate avea loc o deschidere pentru scriere deoarece sem=0;
- nu poate avea loc o deschidere pentru citire deoarece nrcit=0 şi sem=0;
- nu poate avea loc o închidere pentru citire deoarece nrcit=0.
Dacă suntem în starea (0,n;0) cu n>0:
- poate avea loc o deschidere pentru citire, trecându-se în starea (0,n+1;0);
- nu poate avea loc o deschidere pentru scriere, deoarece sem=0;
- nu poate avea loc o închidere pentru scriere, deoarece nrscr=0;
- dacă n>1, printr-o închidere pentru citire se trece în starea (0,n-1;0);
- dacă nrcit=1, atunci printr-o închidere pentru citire se trece în starea (0,0;1)
(dacă nu există procese blocate la semaforul sem) sau în una din stările (1,0;0),
(0,1;0) (dacă există procese blocate la semaforul sem).
Din analiza de mai sus rezultă validitatea procedurilor deschide şi inchide,
precum şi faptul că sem este un semafor binar.

Proiecte

Vor trebui prezentate 3 proiecte:


- unul cu monitoare
- unul cu semafoare
- unul cu canale
- unul de calcul paralel
dintre care cel puţin două cu interfeţe grafice.

S-ar putea să vă placă și