You are on page 1of 5
LEZIONE 5 PROBLEMI “CLASSICI” DELLA PROGRAMMAZIONE CONCORRENTE: PRODUTTORI/CONSUMATORI IN QUESTA LEZIONE IMPAREREMO... le caratteristiche del problema produttori/consumatori | risolvere il problema produttori/consumatori Generalita Fsiste un certo numero di problemi “elassici” della programmazione concorrente che rappresene tano la casistica completa delle diverse situazioni di concorrenza; possono essere suddivisi in tre ‘gruppi, dei quali riportiamo il nome con il quale sono indicati nella letteratura informatica: » produttore/eonsumatore (produeer/eonsumer) = I produtiore= 1 consumatore ~ butier limitato o circolare (bounded buffer) =» produtort ~n consumator! letoori e soritteri (readers/writers) ? filosofi a cena (dining philosophers) In questa lezione descriveremo il primo dei tre problemi nelle sue diverse situazioni mentre riman- diamo gh altri alle successive lezioni @ Produttore/consumatore I problema del produttore/consumatore & un modello classico per lo studio delle sincronizzazione nei sistemi operativi dove piit processi devono coordinarsi per scambiarsi delle informazioni Un produttore ¢ un consumatore Nella sua formulazione pit: semplice sono presenti due process, il primo, detto produttore, che vuole inviare informazioni scrivendo un dato (o un messaggio) in un’area di memoria condivisa (nei nostri esempi in una varishile) e il secondo, chiamato consumatore, che vuole prelevare i valori prodotti dal primo e “consumarli”. > Un tipico esempio & la gestione della stampa, D Butler dove vengono inviati i dati al processo che si oveupa di stamparh. I sistema @ soggetto ai seguenti vincoli: » il produttore pud derositare un dato alla voltae non pud serivere un nuovo messadgio se il consti= matore non ha ancora raccolto il precedente; » il consumatore pad leggere un dato alla volta e, naturalmente, solo dopo che sia stato prodotto dal proceso produttore ¢ non deve leggere cue volte lo stesso valore: » nel sistema non devono verificarsi situazioni di deadlock. I due provessi deveno seambiarsi dei messadgi per comuntcarsi rispettivamente la dispontbilita del dato da leggere e Vavvenuta lettura di dato e, quindi, la possibilita di produrre il successivo: ciascuno processo deve attendere un segnale da parte dell'altro processo. ‘Una possibile soluzione in pseudo codifica che utilizza due semafori é la seguente: semaphore piano = 0 // aii‘inizio 12 pumer non @ pienso (sem.r0s50) semaphore vuoto = 1 i/ al1tinizio i1 bafer @ vuoto (sen. verde) int sumer; Processo preduci(int dato) Pioucto) J) quando i1 buffer & muots bufer = dato V(pieno) // segnela che il bufer @ pieno sn Processo consuma() P(pieno) // qaando ii buffer @ pieno ‘ v(vactey )/ sagnala che i1 buter & vuste Il fanzionamento @ abbastanza semplice: vengono utilizzati due semafori il primo che indica quan- doil buffer é pieno e quindi verra settato del produttore, il secondo che indica quando il dato @ stato consumato, ¢ sara quindi settato dal consumatore: alla fine delle operaziont ogni processo deve avvisare l'altro del cambiamento di stato e lo fara resettando il semaforo opportune: » semaphore vuoto = preduttore lo mette a 0 quando inizia a inserire un dato = consumatore lo mette a 1 quando ha tolto un dato » semaphore pieno ~ produtiore lo mette a 1 quando ha inserito un dato ~ consumatore lo mette a 0 quando inizia a togliere il dato Un produttore e un consumatore ¢ buffer eireolare Nel caso pith generale la memoria condivisa pud contenere pit dati (o messaggi) e viene realizzata per esempio con un buffer eireolare implementaco con un vettore di lunghezza DIM_BUFFER. 0 togi met DIM_BUFFER- _— _— ett togi MModifichlamo le condiziont dl funzionamencto in: ¥ il produttore non pud inserire un messagsio nel buffer se questo & pieno: 9 il consumatore non pud prelevare un messaggio dal buffer se questo & vuoto et DiMM_BUFFER.1 Introduciame due puntatori, mettie to» — gyEFER alt, che individuano rispetcivamente la Set prima porzione vuota e piena del buffer dove inizialmente viene posto metti = jae cota FIFO con dmensione I due puntatori metti e togli che si spo- DIM_BUFFER stane attorng all'array realizzano una coda cireolare FIFO con una dimensio- ne massima prefissata (DIM_EUFFER): yengono utilizzati in modo ciclice (mo: dulo dell'ampiezza del buifer mediante Yoperatore %) Una coditica del proceso produttore & la seguente: DIM_BUFFER tog Provesso produci(int dato) P(wuoto) J/ qaanéo 41 bufer ha 2lmeno uno spazio woto bafer[metti] = dato; /| aggiornamento indica: quando metei = (metti +1) & DIM_ BUFFER; Y{pieno) // segnela che i1 buffer @ pieno { arrive OIMBUFFER #i ritorna a 0 Una codifica del processo consumatore é la seguente: Processo consuma() inizio P(pieno} W/ quando 1 bufer & pieno togl: =(sogli+1)$D1M_ BUFFER; u(weoto) J/ gagnala che i1 bafer & vuoto // aiitinizio G2 bufler ha posti liberi // dati da leggare presenti nel bufer Pia produttori ¢ pit consumatori Estendiamo ora al caso generale, dove possono essere presenti pitt produttori e pitt consumatori questa nuova situazione pud comportare nuove necessita di sineronizzazione in quanto ora non & sufficiente la sincronizzazione appena descritta, poiché abbiamo due tipi di problematiche: sincronizzare | produttort e { consumatori, cos! come nel caso precedente, verificando che se qua lungue dei produttori volesse produrre ma non ci fosse la disponibilita di un butfer libero, questo deve sospendersi e attendere che uno qualungue dei consumatori consumi un dato liberando cosi lo spazio di un buffer; incronizzare gi n produttori fra di loro e gli m consumatori fra di loro perché in questa condi zione anche le varicbili togli e metti sono condivise e quindi divengono di fatto risorse comuni. Se per esempio due produttori contemporaneamente effettuano la P(V) ed entrano nella regione cri- tica, molto probabilmente scriveranno nella stessa cella ed eseguiranno in modo errato l'operazione: metti = (metti + 1) & DIM BUFFER; Dobhiamo risolvere questo conflitto nei preduttor cost come lo ritroviamo simile anche net lettort, che potrebbero leggere contemporaneamente lo stesso dato. Inizialmente definiamo i semafori « il buffer comune: senapnore yuoto = DIM.BUFFER // ail‘inizio 11 puter na posti 1iperi eenaphore piene = 0 // aati da leggare preseati nel bufer bufer array [01M BUFFER) Per i procuittor! useremo quindi una procedura come quella di seiaite riportata, che dopo aver a certata lesistenza di almeno un buffer libero, serializza tra i produttori accesso alla variabile metti in modo da garantire Ja mutua esclusione al suo accesso da parte dei produttori che richiedono di aggiornarla. Proceso produei(int dato) sensphore mutex? = 1 // senaforo che regola i produttori int tempo; (uote) {/ quando it buffer ha spezio libero (mutex?) // blocea l'accesso alla RC agli altri produttori tempo = mete: // salva il valore corrente di metti metti =(mettiel)€ DIN SUFFER; // aggiornamento dell‘incice vy (maten>) // Libera iraccessc alla RC agli altri produetori buferimetti} = dato // insarisci i1 move dato V{pieno) // secnala che it buffer @ pieno Per i consumatori useremo quindi une procedura simile, di seguito riportata: Proceso consums( ) int tempo; semaphore mutex = 1 // senaforo che regols 1 consunator! (piano) J) quands 32 boffer & pier (mutex?) // blocea 1'sccesso alla RC agli altri produttori ‘tempo stogli // salva il valore corrente di metti togli =(togli+i)4D1M_SuPFER; (mutex?) // Libera 1'accesso alla RC agli altri produttori vivucte) // segnaia che it buffer @ vaoto Bisogna stare attenti a non precludere il parallelismo del riempimento del buffer da parte dei pro- dutori, soprattutto se le operazioni di serittura nel buffer sono piuittosto lente: se cos) fosse, tutti 4h altri produttori rimarrebbero in attesa per lungo tempo anche in presenza di posti liberi, e anche tutti { consumatori rimarrebbero fermi, non avendo nulla da “consumare”, Per questo motivo sono state portate al di fuori della regione critica le operazioni di deposito e di prelievo dei dati.

You might also like