un semaforo e' una variabile o un flag che gestisce l'accesso a delle
risorse del sistema
esistono 2 tipi:
semafori semplici e generalizzati.
in un caso semplice ci sono soli 2 threads
con la risorsa che ne accetta solo uno per volta
in altri casi, dove la risorsa puo avere piu' di un threads alla volta,
si parla di semaforo generalizzato che non e' altro che una variabile che puo'
assume un valore massimo, e in una variabile si segna quanti
threads stanno accedendo a una risorsa
finche il valore attuale e' al di sotto del massimo un altro threads puo'
accedere a quella risorsa altrimenti deve aspettare che si liberi
nel caso semplice
un esempio:
abbiamo due processi A e B che vogliono accedere a C
C e' una locazione di memoria dove si
vuole che un processo(A) salvi un dato e un altro(B)
legga il dato per rielaborarlo.
si puo' intuire che C accetta solo uno alla volta e che se devono
essere sincronizzati A e B devono avere un flag di gestione
altrimenti A rischia di scrivere due(o piu') volte il dato prima che B legga
oppure B rischia di Leggere 2(o piu') volte il dato prima che A lo aggiorni.
quindi Presupponendo che parta A(visto la logica)
il flag sara' =0
A dovra'
se il flag e' a 0
scrivere su C
cambiare il flag e metterlo a 1
altrimenti continua a controllare il flag finche'
sara' uguale a 0
B dovra'
se il flag e a 1
leggere da C
cambiare il flag e metterlo a 0
altrimenti continua.
A si chiama Produttore
B si chiama consumatore
il flag e' detto semaforo.
in realta' anche per un caso come questo si usano 2 semafori, uno per A e uno
per B, inoltre si puo sentir parlare di procedure P(s) e V(s);
la prima controlla finche una risorsa e' libera e poi la setta a non piu'
libera mentre la V(s) libera la risorsa occupata.
la P(s) sara quindi chiamata in testa ad un thread e la V(s) in coda;
P(s) fa questo:
ripeti
attendi
finche s=1;
s=0;
cioe' attende che la risorsa si liberi e poi la setta a 0.
V(s) setta s=1;
quindi nel caso produttore/consumatore abbiamo 2 semafori
s1 ed s2 settati rispettivamente a 1 e a 0
il produttore fara':
generazione dati
P(s1)
scrivi nel buffer
V(s2)
il consumatore invece fara'
P(s2)
lettura del buffer
V(s1)
manipolazione dati
.
Il caso del semaforo generalizzato si puo' avere quando, ad esempio,il
produttore e piu' veloce del consumatore, quindi puo'
riempire piu di un buffer al colpo.
in questo caso la P(s) assume la forma:
ripeti finche s>0;
s=s-1;
cioe' un threads guarda se c' e' una risorsa disponibile e poi la occupa
V(s) sara' :
se s<MAX
s=s+1;
dove MAX e' il numero massimo di risorse allocabili.
il consumatore se ad un certo punto il produttore si blocca leggera' prima
l' ultimo buffer scritto poi il penultimo, e cosi' via fino al primo.