Table of Contents
Esempi svolti di programmazione concorrente
In questa sezione verranno presentati una serie di esercizi svolti di programmazione concorrente utilizzando uno pseudo linguaggio simile al C. Le funzioni P()
e V()
hanno le stesso comportamento delle funzioni POSIX sem_wait()
e sem_post()
, rispettivamente. Si ipotizzi che sia possibile definire ed inizializzare un semaforo con uno pseudo-statement del tipo: sem_t <nome_semaforo> = <valore_iniziale>;
. Le variabili con nome m
(m
, m1
, m2
) rappresentano dei semafori utilizzati come dei mutex.
Nello svolgimento di tutti gli esercizi si ipotizzi che i thread che realizzano il programma concorrente siano stati già creati. Ad un programma completo occorrerebbe aggiungere alle soluzioni qui riportate la la funzione pthread_create() per la creazione dei thread.
Esempio 1 (Semaforo con priorità)
Un numero imprecisato di processi Pi possono chiamare le funzioni int p(int prio, int value_p)
e int v(int value_v)
. Esse dovranno simulare, a partire dai semafori ordinari (su cui è supposto un accodamento FIFO), il comportamento di semafori che accodano in base alla priorità prio
argomento della funzione p()
(0 < = prio < = 9
, prio=9
è la massima priorità).
Il comportamento delle due funzioni è di seguito descritto:
int p(int prio, int value_p)
: quando il valore del semaforo è un numero maggiore di 0 la funzione decrementa di un'unità tale valore e restituisce 0. Nel caso in cui il semaforo sia uguale a 0 la funzione è bloccante. Quando sbloccata dav()
,p()
dovrà restituire al chiamante il valorevalue_v
passato come argomento alla funzionev()
.int v(int value_v)
: se eseguita quando non vi è nessun processo bloccato sup()
, la funzione incrementa di un'unità il valore del semaforo e restituisce il valore 0. Nel caso in cui esista almeno un processo bloccato sup()
, la funzione dovrà liberare il processo bloccato con maggior priorità e restituire il valorevalue_p
passato come argomento alla funzionep()
. Nel caso di più processi bloccati con la stessa priorità la scelta del processo da sbloccare è libera.
Il semaforo ha valore iniziale 2
.
Si realizzino le funzioni int p(int prio, int value_p)
e int v(int value_v)
.
Esempio:
- P1 chiama
p(3, 10)
che portasem=1
e restituisce0
- P1 chiama
p(4, 20)
che portasem=0
e restituisce0
- P1 chiama
p(4, 30)
e si blocca - P2 chiama
p(7, 15)
e si blocca - P3 chiama
v(14)
che sblocca il processo P2, in quanto7 > 4
. La funzionep()
restituisce a P2 il valore14
e la funzionev()
restituisce a P3 il valore15
- P3 chiama
v(35)
che sblocca il processo P1. La funzionep()
restituisce a P1 il valore35
e la funzionev()
restituisce a P3 il valore30
- P3 chiama
v(16)
che portasem=1
e restituisce0
- soluzione_es1.c
#define NPRIO 10 sem_t wait[NPRIO]={0,0....}; int nBlockedPrio[NPRIO]={0,0,...}; int nBlocked=0; int pass = 0; int sem=2; sem_t sync=0; int p(int prio, int value_p){ int ret; P(m); if(sem==0){ nBlocked++; nBlockedPrio[prio]++; V(m); P(wait[prio]); ret=pass; pass=value_p; V(sync); return ret; }else{ sem--; V(m); return 0; } } int v(int value_v){ int ret; P(m); if(nBlocked){ for(i=NPRIO-1; i>=0; i--){ if (nBlockedPrio[i]){ nBlockedPrio[i]--; nBlocked--; pass = value_v; V(wait[i]); P(sync); ret=pass; V(m); return ret; } } }else{ sem++; V(m); return 0; } }
Esempio 2 (Messaggio broadcast)
Un numero imprecisato di processi Ci possono chiamare la funzione int bConsume()
e un processo P può chiamare la funzione void bProduce(int value)
.
Il comportamento delle due funzioni è di seguito definito:
int bConsume()
è bloccante nell'attesa che venga chiamata dal processo P la funzionebProduce(int value)
. La funzionebProduce(int value)
, quando chiamata, sbloccherà tutti i processi bloccati suint bConsume()
, la quale dovrà restituire ad ogni processo sbloccato il valorevalue
passato come argomento alla funzionebProduce(int value)
.bProduce(int value)
, nel caso ci siano dei processi bloccati suint bConsume()
, dovrà sbloccarli passando ad essi il valorevalue
. Nel caso nessun processo sia bloccato sulla funzioneint bConsume()
, essa dovrà essere bloccante nell'attesa che un processo eseguaint bConsume()
. Una volta sbloccata, la funzionebProduce(int value)
dovrà passare il valorevalue
alla funzioneint bConsume()
la quale, non bloccandosi, lo restituirà al processo chiamante.
Si realizzino le funzioni int bConsume()
e bProduce(int value)
.
Esempio:
- C1 chiama
bConsume()
e si blocca - C2 chiama
bConsume()
e si blocca - P chiama
bProduce(21)
che sblocca i processi C1 e C2 bloccati subConsume()
. La funzionebConsume()
restituirà a entrambi i processi il valore 21 - P chiama
bProduce(12)
e si blocca - C2 chiama
bConsume()
che sblocca il processo P e restituisce a C2 il valore 12 - C1 chiama la funzione
bConsume()
e si blocca
- soluzione_es2.c
int pBlocked=0, cBlocked=0, pass; sem_t pWait=0, cWait=0; sem_t m=1, m2=1; void bProduce(int value){ int localCBlocked, c; P(m); if(cBlocked==0){ pBlocked++; V(m); P(cWait); } localCBlocked = cBlocked; pass = value; for(c=0; c<localCBlocked; c++) V(pWait); } int bConsume() { int ret; P(m); cBlocked++; if(pBlocked){ pBlocked--; V(cWait); }else{ V(m); } P(pWait); P(m2); cBlocked--; ret = pass; if(cBlocked==0) V(m); V(m2); }
Esempio 3 (Moltiplicazione tra processi)
Un numero imprecisato di processi Pi possono chiamare la funzione int val(int v)
e un processo M può chiamare la funzione void mul(int m)
.
Il comportamento delle due funzioni è di seguito definito:
int val(int v)
è bloccante nell'attesa che venga chiamata dal processo M la funzionevoid mul(int m)
. Quando la funzionevoid mul(int m)
è chiamata dal processo M, essa dovrà sbloccare tutti i processo P bloccati fino a quell'istante suint val(int v)
. Ad ogni processo P la funzioneint val(int v)
dovrà restituire il valorev
moltiplicato per il valorem
.void mul(int m)
, oltre al comportamento descritto nel precedente punto, nel caso in cui non ci sia nessun processo bloccato suint val(int v)
, essa dovrà essere bloccante nell'attesa che un processo di tipo P chiami la funzioneint val(int v)
. Quando tale evento accade il processo M dovrà essere sboccato e la funzioneint val(int v)
, chiamata dal processo P, dovrà restituire immediatamente il valorev*m
.
Si realizzino le funzioni int val(int v)
e void mul(int m)
.
Esempio:
- P1 chiama
val(3)
e si blocca - P2 chiama
val(5)
e si blocca - M chiama
mul(2)
che sblocca i processi P1 e P2 bloccati suval()
. Le due funzionival()
restituiranno rispettivamente ai processi chiamanti, P1 e P2, i valori3*2=6
e5*2=10
- M chiama
mul(3)
e si blocca - P2 chiama
val(4)
che sblocca il processo M e restituisce immediatamente a P2 il valore4*3=12
- soluzione_es3.c
sem_t m1=1, m2=1, sync=0, syncMul=0; int n=0, s=0, mean=0, meanBlock=0; int sum(int x){ int ret; P(m1); if(meanBlock){ s=x; V(syncMul); ret = mean; P(sync); V(m1); return ret; }else{ s += x; n++; V(m1); P(sync); ret = mean; P(m2) n--; if(n==0) V(m1); V(m2); return ret; } } mul(int m){ int c; P(m1); if(n>0){ mean = s*m; for (c=0; c<n;c++){ V(sync); } }else{ meanBlock+=1; V(m1); P(syncMul); mean = s*m; V(sync); } }
Esempio 4
Un numero imprecisato di processi Pi possono chiamare le funzioni int wait(int w_prio, int max_proc)
e int sblock(int s_prio, int value)
. I parametri w_prio
e s_prio
possono assumere valori compresi tra 0 e 9.
Il comportamento delle due funzioni è di seguito definito:
int wait(int w_prio, int max_proc)
è bloccante se il numero totale di processi da essa bloccati fino all'istante della sua chiamata è inferiore amax_proc
, alternativamente restituisce il valore -1. Se i processi bloccati suwait
vengono sbloccati dalla funzionesblock
, la funzionewait
deve restituire il valorevalue
passato come argomento alla funzionesblock
.int sblock(int s_prio, int value)
sblocca tutti i processi bloccati dalla funzionewait
con valorew_prio
uguale as_prio
. La funzionewait
dovrà restituire a tutti i processi sbloccati il valorevalue
. La funzionesblock
restituisce il numero di processi sbloccati, -1 nel caso in cui nessun processo sia sbloccato.
Si presti particolare attenzione al fatto che il valore value
sia restituito correttamente a tutti i processi sbloccati e che i processi Pi, che eseguono la funzione wait
dopo una funzione sblock
, siano correttamente bloccati dalla funzione wait
.
Si realizzino le funzioni int wait(int w_prio, int max_proc)
e int sblock(int s_prio, int value)
.
Esempio:
- P1 chiama
wait(3,2)
e si blocca - P2 chiama
wait(3,4)
e si blocca - P3 chiama
wait(5,2)
e restituisce immediatamente -1, in quanto il numero totale di processi bloccati è 2 che non è inferiore al valoremax_proc=2
- P3 chiama
wait(5,3)
e si blocca - P4 chiama
sblock(3,6)
che sblocca i processi P1 e P2 i quali restituiscono entrambi il valore 6. La funzionesblock
restituisce, invece, il valore 2 - P1 chiama
sblock(2,7)
la quale restituisce immediatamente il valore -1 perché non sblocca nessun processo
- soluzione_es4.c
#define N_CLASSES 10 int value_pass; int nwait = 0; int nwait_prio[N_CLASSES] = {0,0,...}; sem_t sync[N_CLASSES] = {0,0,...}; sem_t mutex = 1, mutex2 = 1; int wait(int w_prio, int max_proc){ int res; P(mutex); if (nwait >= max_proc){ V(mutex); return -1; }else{ nwait_prio[w_prio]++; nwait++; V(mutex); P(sync[w_prio]); P(mutex2); res = value_pass; nwait--; nwait_prio[w_prio]--; if (nwait_prio[w_prio]==0) V(mutex); V(mutex2); return res; } } int sblock(int s_prio, int value){ int c, res; P(mutex); if (nwait_prio[s_prio] > 0){ res = nwait_prio[s_prio]; value_pass = value; for(c=0; c<res; c++){ V(sync[w_prio]); } }else{ res = 0; V(mutex); } return res; }
If you found any error, or if you want to partecipate to the editing of this wiki, please contact: admin [at] skenz.it
You can reuse, distribute or modify the content of this page, but you must cite in any document (or webpage) this url: https://www.skenz.it/it/informatica/esercizi_svolti_di_programmazione_concorrente