4.6 KiB
Semafori e Sezioni critiche condizionali (continua)
Implementazione di Accesso condizionale
Inizializzazione:
struttura dati R;
semaphore mutex = 1;
semaphore sem = 0; //blocca accesso a sc
int csem = 0; //contatore per #processi bloccati su sem
Due operazioni: Richiesta e Rilascio
Soluzione classica
Richiesta
Un processo cerca di entrare nella SC, verificare la condizione (C) ed eseguire l'operazione
public void request () {
// ingresso in SC
P (mutex);
// verifica della condizione
// finche' non e' verificata, loop
while (!C) {
csem++;
V (mutex); // libero la SC
P (sem); // aspetto che sem sia libero
P (mutex); // rientro in SC
}
// se arriva qui, C e' verificata e il processo e' nella sezione critica
<< operazione critica >>
V (mutex)
}
NOTA: E' possibile che un processo entri nella sezione critica mentre l'altro e' dentro il loop while. Questo fenomeno si chiama spiffero.
Rilascio
Rilascio delle risorse dopo aver eseguito l'operazione critica.
public void release () {
P (mutex);
<< operazione critica >>
if (csem > 0) {
csem--;
V (sem);
}
V (mutex);
}
Soluzione alternativa
Questa soluzione, per quanto piu' efficiente,e' utilizzabile solo quando l'operazione critica cambia il valore della condizione C, permettendo a un altro processo di accedervi. C deve essere quindi condivisa tra processi.
Due operazioni, come nella soluzione classica.
Richiesta
public void request () {
if (!C) { // simile alla soluzione classica
csem++; // niente loop perche' assumiamo che eseguire l'OC cambi il valore di C
V (mutex);
P (sem);
csem--; // V (sem) puo' essere chiamato qui, o durante il rilascio
}
<< operazione critica >>
V (mutex);
}
Rilascio
public void release () {
P (mutex);
<< operazione critica >>
if (csem > 0) {
V (sem);
} else {
V (mutex);
}
}
(vedi esempio con pool di risorse)
Producer and Consumer (produttore e consumatore) - Semaforo contatore
Si presenta qui una soluzione alternativa al problema del produttore e consumatore, che utilizza un semaforo contatore per gestire un buffer circolare; Si assume 1 produttore e 1 consumatore.
Inizializzazione
semaphore empty = N;
semaphore full = 0;
T buffer[N];
int head = 0;
int tail = 0;
N e' la dimensione del buffer, e empty e' inizializzato ad essa perche' tutte le celle del buffer sono vuote all'inizializzazione. head e tail sono due contatori, tail per il producer e head per il consumer, che indicano la cella occupata.
Producer (invio di dati)
public void send (T data) {
P (empty);
buffer[tail] = data;
tail = (tail++) % N;
V (full);
}
Consumer (ricezione di dati)
public T receive () {
P (full);
T data = buffer[head];
head = (head++) % N;
V (empty);
return data;
}
Si nota come il buffer circolare implichi gli incrementi di head e tail in modulo N.
Dimostrazione di Correttezza
- Siano:
nd(t) = # inserimenti (depositi) da parte del produttore
ne(t) = # estrazioni (ricezioni) da parte del consumatore
- La precedente dimostrazione del problema producer-consumer aveva portato:
0 <= nd(t) - ne(t) <= 1
-
Perche' non possono esserci piu' estrazioni che inserzioni, e le inserzioni devono essere al massimo 1 in piu' delle estrazioni.
-
Da qui, utilizzando l'invariante semaforico:
NP (empty, t) <= NV (empty, t) + N
-
il che rispecchia l'inizializzazione del problema.
-
Per dimostrare la correttezza, supponiamo che esista un istante t in cui il producer deposita e il consumer estrae. Dobbiamo quindi dimostrare che head != tail, in quanto non e' possibile che producer e consumer lavorino sulla stessa cella del buffer.
-
Siano:
p1(t) = # incrementi in coda (tail)
p2(t) = # incrementi in testa (head)
- Da questo, la tesi puo' essere riscritta come:
1 <= p1(t) - p2(t) <= N-1
- Supponendo che entrambi i processi debbano ancora uscire dalla sezione critica:
p1(t) = NV(full, t) = NP (empty, t) - 1
p2(t) = NV(empty, t) = NP (full, t) - 1
- Da cui si puo' utilizzare l'invariante semaforico per ottenere:
p2(t) = NP(full,t) - 1 <= NV(full, t) - 1 <= p1(t) - 1 <= NP(empty, t) - 2 <= NV(empty, t) - 2 <= NV(empty, t) - 2 + N <= p2(t) + N - 2
- Considerare solo i termini p1, p2:
p2(t) <= p1(t) - 1 <= p2 (t) + N - 2
- Possiamo sommare 1 a tutti i termini senza cambiare la validita' della disequazione:
p2(t) + 1 <= p1(t) <= p2(t) + N - 1
- Che, sottraendo a ogni termine p2(t), risulta:
1 <= p1(t) - p2(t) <= N - 1
- Che e' proprio la tesi di partenza della dimostrazione di correttezza. CVD