capitolo 25 – lettori / scrittori
Transcript
capitolo 25 – lettori / scrittori
CAPITOLO 25 – LETTORI / SCRITTORI Un altro problema classico che si incontra quando si tratta del tema della concorrenza e sincronizzazione e che è utile sia a scopo didattico che come termine di paragone con i problemi di progettazione è quello che viene chiamato problema dei lettori/scrittori. Il problema ha la seguente definizione: c’è un’area di dati condivisa fra vari processi (questa può essere un file, un blocco di memoria, o un banco di registri del processore) ed esistono dei processi che concorrono per l’utilizzo dell’area: alcuni possono solo leggere i dati (lettori), altri possono solo scrivere (scrittori). Prima di analizzare il problema dei lettori/scrittori è bene evidenziare le differenze con altri due classici problemi: il problema generale della mutua esclusione e il problema del produttore/consumatore. Nel problema dei lettori/scrittori, i lettori non possono essere anche scrittori, e gli scrittori non possono leggere. Se consideriamo come caso più generale quello in cui tutti i processi possono sia leggere che scrivere è possibile risolvere il problema dichiarando la porzione nella quale i processi accedono ai dati come sezioni critiche e quindi utilizzare la soluzione generale per la mutua esclusione. La ragione per cui ci si pone nel problema particolare è che si possono trovare soluzioni più efficienti rispetto alla soluzione generale che risulterebbe essere di una lentezza inaccettabile in questo caso. Se ad esempio si considera come area condivisa il catalogo di una biblioteca avremo gli utenti normali che leggono il catalogo per cercare un libro e un paio di bibliotecari che si occupano di aggiornare il catalogo. Applicando la soluzione generale, ogni accesso al catalogo sarebbe trattato come sezione critica per cui un solo lettore per volta avrebbe accesso al catalogo causando così un ritardo intollerabile. Allo stesso tempo è importante impedire agli scrittori di interferire tra loro, ed è necessario impedire ai lettori di leggere mentre ci sono delle operazioni di scrittura in corso, per evitare l’accesso ad informazioni non corrette. È possibile considerare il problema del produttore/consumatore semplicemente come un caso speciale del problema dei lettori/scrittori con un solo scrittore (il produttore) e un solo lettore (il consumatore)? La risposta è no, perché il produttore non è solo uno scrittore, ma deve leggere i puntatori della coda per stabilire dove mettere il prossimo elemento, e deve stabilire se il buffer è pieno; analogamente il consumatore non è solo un lettore, perché deve modificare i puntatori della coda per riflettere il fatto che un elemento è stato rimosso dal buffer. Vincoli del problema Verranno di seguito esaminate due soluzioni del problema, prima però si riassumono le condizioni che devono essere soddisfatte: 1. più lettori possono leggere il file contemporaneamente. 2. solo uno scrittore alla volta può scrivere nel file. 3. se uno scrittore sta scrivendo nel file, nessun lettore può leggerlo 4. gli scrittori non possono leggere 5. i lettori non possono essere anche scrittori Soluzione del problema con priorità ai processi lettori Nella soluzione che usa i semafori vengono utilizzate le seguenti variabili e semafori: 1. la variabile globale numlettori è usata per tenere traccia del numero di lettori; 2. il semaforo x è usato per assicurarsi che numlettori sia aggiornata correttamente; 3. il semaforo wsem serve per garantire la mutua esclusione: i processi scrittori quando accedono all’area condivisa utilizzano questo semaforo per impedire agli altri processi (sia scrittori che lettori) di accedervi. Anche i processi lettori utilizzano il semaforo wsem, infatti il primo lettore che tenta di leggere verifica che non ci siano scrittori nell’area condivisa controllando il valore di wsem e lo setta per impedire ai processi scrittori di accedere all’area. Altri processi lettori che intendono accedere all’area condivisa non si fermano su wsem ma possono eseguire l’operazione di lettura. Soluzione del problema usando i semafori: priorità ai lettori procedure scrittore; begin repeat wait(wsem); SCRIVI_UNITÀ; signal(wsem); forever; end; procedure lettore; begin repeat wait(x); numlettori:=numlettori+1; if numlettori=1 then wait(wsem); signal(x); LEGGI_UNITÀ; wait(x); numlettori:=numlettori-1; if numlettori=0 then signal(wsem); signal(x); forever; end; Priorità agli scrittori Nella soluzione precedente i lettori hanno la priorità: quando un lettore inizia ad accedere ai dati, i lettori possono mantenere il controllo dell’area dati finchè c’è un lettore attivo, quindi gli scrittori rischiano un’attesa perenne. Di seguito viene mostrata una soluzione che impedisce a nuovi lettori di accedere ai dati se qualche scrittore ha dichiarato di voler effettuare una scrittura; sono stati aggiungi i seguenti semafori e variabili per gli scrittori: 1. un semaforo rsem che blocca i lettori quando c’è almeno uno scrittore che desidera accedere ai dati; 2. una variabile numscrittori che controlla il numero di scrittori per gestire rsem; 3. un semaforo y che controlla l’aggiornamento di numscrittori; 4. un semaforo z che impedisce la formazione di una lunga coda di lettori sul semaforo rsem; in questo modo i processi scrittori che intendono accedere all’area condivisa possono saltare la coda. Con l’utilizzo del semaforo z ad un solo lettore è permesso di entrare nella coda di rsem mentre tutti gli altri attendono su z prima di passare al semaforo rsem. Analizziamo i due processi procedure scrittore; begin repeat wait(y); il processo verifica il semaforo y, se è positivo lo decrementa e accede alla variabile numscrittori, altrimenti attende numscrittori:=numscrittori+1; incrementa la variabile numscrittori if numscrittori=1 then se numscrittori ha valore 1 allora wait(rsem); controlla il semaforo rsem e se è positivo lo decrementa (impedendo ai lettori di accedere all’area condivisa) signal(y); rilascia la variabile numscrittori incrementando il semaforo y wait(wsem); se wsem è positivo, lo decrementa e accede all’area condivisa (in questo modo impedisce ad altri scrittori di accedere contemporaneamente) SCRIVI_UNITA’; signal(wsem); wait(y); Scrive Incrementa il semaforo wsem e rilascia l’area condivisa il processo verifica il semaforo y, se è positivo lo decrementa e accede alla variabile numscrittori, altrimenti attende. numscrittori:=numscrittori-1;Decrementa la variabile numscrittori if numscrittori=0 then signal(rsem); signal(y); forever end; Se numscrittori è uguale a zero vuol dire che non ci sono scrittori in attesa e incrementa il semaforo rsem consentendo ai lettori di leggere nell’area condivisa Incrementa il semaforo y consentendo l’uso da parte degli altri processi della variabile numscrittori procedure lettore; begin repeat wait(z); wait(rsem); wait(x); numlettori:=numlettori+1; if numlettori=1 then wait(wsem); signal(x); signal(rsem); signal(z); LEGGI_UNITA’; wait(x); numlettori:=numlettori-1; controlla il semaforo z, se è positivo lo decrementa e continua, altrimenti attende. Controlla il semaforo rsem, se è positivo vuol dire che non ci sono scrittori in attesa di leggere controlla il semaforo x, se è positivo lo decrementa e può accedere alla variabile numlettori. Incrementa la variabile numlettori Se numlettori è uguale a 1 allora controlla se ci sono scrittori nell’area condivisa. Attende che wsem gli dia il via libera per procedere alla lettura Incrementa i semafori x, rsem e z e consente ad altri processi di effettuare i controlli necessari per accedere all’area condivisa Effettua la lettura dell’area condivisa Controlla il semaforo x, se positivo può accedere alla variabile numlettori che andrà a decrementare. if numlettori=0 then Se numlettori è uguale a zero allora signal(wsem); ncrementa il semaforo wsem in modo da indicare che non ci sono lettori nell’area condivisa. signal(x); forever end; Incrementa il semaforo x concedendo ad altri processi che ne fanno richiesta di accedere alla variabile numlettori