/////////////////////////////////// /// ESERCIZI SUI SEMAFORI /// /// Stefano Ferretti /// ///////////////////////////////////
|
|
|
- Dionisia Pugliese
- 9 anni fa
- Visualizzazioni
Transcript
1 /////////////////////////////////// /// ESERCIZI SUI SEMAFORI /// /// Stefano Ferretti /// /////////////////////////////////// Soluzione al Problema del Barbiere Possiamo vedere questo problema come un problema di produttore-consumatore. I produttori sono i clienti, che producono se stessi. E il consumatore è il barbiere, che consuma un cliente alla volta. Innanzitutto, dobbiamo descrivere la vita del barbiere e dei clienti, perchè non è esattamente specificata: process Barbiere { while (true) { attendi cliente // taglia capelli libera poltrona process Cliente { verifica se c'è posto, altrimenti esci attendi turno // fatti tagliare i capelli E questo è il programma: Semaphore mutex = new Semaphore(1); Semaphore clientedisponibile = new Semaphore(0); Semaphore poltrona = new Semaphore(0); int sedielibere = N; process Barbiere { while (true) { clientedisponibile.p(); // taglia capelli poltrona.v(); process Cliente { if (sedielibere==0) { return; sedielibere--; clientedisponibile.v(); poltrona.p(); sedielibere++; // Fatti tagliare i capelli // e poi esci
2 Nome: Semafori Barriera abbiamo già visto (nei lucidi) un esempio in cui 2 processi si sincronizzano attraverso una sorta di "barriera" fatta di una sequenza di operazioni V() e P() su 2 semafori. Ora se ci chiedessimo di sincronizzare tramite una barriera 3 processi P, Q, R, come potremmo fare? // riporto qui la versione a 2 processi per rinfrescarci la memoria s1, s2 semafori generali iniz. a 0 process P process Q s2.v() s1.v() // P da il via a Q e viceversa s1.p() s2.p() // P aspetta il via da Q e viceversa // fine versione a 2 processi s1, s2, s3 semafori generali iniz. a 0 process P process Q process R s2.v() s1.v() s1.v() s3.v() s3.v() s2.v() s1.p() s2.p() s3.p() s1.p() s2.p() s3.p() Guardando al codice dei tre processi, sembrerebbe che non facciano nulla (non stampano, non fanno calcoli, etc.). Quello che fanno i tre processi è sincronizzarsi uno con l'altro. Dobbiamo immaginare che vi sia del codice prima e dopo le invocazioni delle primitive dei semafori, rappresentato da. Ognuno di loro potrà proseguire solo quando ognuno degli altri avrà eseguito le due V() - ovvero, quando avrà superato il codice che si trova prima. Nome: Semafori Interleaving Il seguente programma concorrente, a seconda dell'interleaving fra i due processi, può stampare una o più stringhe. Indicare quali Nota: tutti i semafori sono generali e inizializzati a 0. process P { s1.v(); print F; s1.p(); s2.v(); s1.p(); print C;
3 process Q { print G; s2.p(); print D; s1.v(); La risposta semplice a questa domanda è: FGDC, GFDC. La risposta completa dovrebbe mostrare il reticolo delle azioni, per dimostrare che effettivamente avete capito cosa state facendo. Nome: Semafori Interleaving 2 Cosa fa questo programma concorrente? (s1, s2 semafori, entrambi inizializzati a 0) process P { s1.v(); s2.p(); print O s2.p(); print D s1.v(); process Q { s1.p(); print L; s2.v(); s2.v(); s1.p(); print E Risposta veloce: stampa "LODE"; la risposta completa dovrebbe fare vedere il reticolo dei possibili passi. Nome: letargo spontaneo Contesto: un processo vuole decidere autonomamente di sospendersi; qualunque altro processo puo' svegliarlo ma solo *dopo* che si e' addormentato Soluzione: Semaphore sosp = 0; // semaforo di sospensione bool sleeping = false; // mutex process P { // che si vuole sospendere autonomamente
4 sleeping = true; sosp.p(); // si sospende perche' S vale sempre 0 process Q { // che vuole svegliare P solo se P e' gia' sospeso? if (sleeping) { sleeping = false; sosp.v(); // scelta possibile: passaggio del testimone o meno Nome: Sezione Critica con risorse condivise Contesto: abbiamo una o piu' risorse condivise e l'accesso a tali risorse deve avvenire in mutua esclusione Soluzione base (già vista ampiamente): // inizializzato a 1 // entrare in sezione critica // lasciare la sezione critica Problema: A volte e' necessario valutare il valore delle risorse condivise per sapere se sia possibile o meno proseguire nella computazione. // chiamiamo la condizione da valutare sui dati condivisi "C(shared)" Soluzione 1 errata: shared dato; if (C(shared) { // NO! Non sono in sezione critica. Dato alterabile. // entrare in sezione critica // lasciare la sezione critica Soluzione 2 errata: Semaphore sosp = 0; // semaforo di sospensione shared dato; // entrare in sezione critica if (C(shared) { else { sosp.p(); // deadlock: nessun altro entrera' mai piu' in sezione critica
5 // lasciare la sezione critica Soluzione 3 (sub-ottimale, alla Java): Semaphore sosp = 0; // semaforo di sospensione shared dato; // entrare in sezione critica while (!C(shared) { // lascio la sezione critica sosp.p(); <-- qua si puo' inserire qualcuno che cambia shared // lasciare la sezione critica Il processo che risveglia il processo sospeso esegue il seguente codice: // entra in sezione critica.. // altera i dati condivisi fra cui shared sosp.v(); // nel dubbio risveglio l'altro processo. Problemi: starvation, spreco di CPU, no FIFO Soluzione 4: (Uso il passaggio del testimone) Semaphore sosp = 0; // semaforo di sospensione shared dato; // entrare in sezione critica if (!C(shared) { // lascio la sezione critica sosp.p(); // ASSUMIAMO DI ESSERE IN SEZIONE CRITICA AL RISVEGLIO // QUA SIAMO CERTI CHE C(shared) VALGA! // lasciare la sezione critica Sia il seguente codice quello eseguito dal processo che risveglia il processo precedente. mutex.p(m); // entra in sezione critica.. // altera i dati condivisi fra cui shared if(c(shared)) { sosp.v(); // RISVEGLIA L'ALTRO PROCESSO E PASSA LA SEZIONE CRITICA. else { Problema ulteriore: La soluzione precedente si basa sull'assunzione che la condizione
6 C(shared) sia valutabile esclusivamente a partire dai dati condivisi. Questo perche' deve essere valutata da entrambi i processi. Come fare quando la condizione coinvolge anche dati non condivisi? Caso A: i dati sono sensibili e non li vogliamo condividere. Soluzione: si torna alla soluzione alla Java. Caso B: i dati non sono sensibili e possono essere condivisi Caso B.1: non mi costa troppo (in spazio) condividere sempre tutti i dati Problemi: a) spreco di memoria (forse) b) aumenta la possibilita' di bug/errori Caso B.2: Osservazione: al processo che deve risvegliare non servono i dati privati di tutti gli altri processi, ma solo quelli del processo che si e' sospeso. Soluzione: il processo che si sospende subito prima di rilasciare la sezione critica condivide i suoi dati privati che servono a testare la condizione. Soluzione alternativa ai filosofi a cena I Semaphore sem_filosofo[5] = {0,., 0; bool b_libera[5] = {1,., 1; // dice se la bacchetta è libera bool sospeso[5] = {0,., 0; // dice se il filosofo attende le bacchette Philo[i] { while (1) { pensa(); prendi_bacchette(i); mangia(); rilascia_bacchette(i); void prendi_bacchette(int i) { if (!b_libera[i]!b_libera[(i+1)%5]) { sospeso[i] = 1; // il processo si sospende sem_filosofo.p(); // PASSAGGIO DEL TESTIMONE b_libera[i] = 0; b_libera[(i+1)%5] = 0; void rilascia_bacchette(int i) { b_libera[i] = 1; b_libera[(i+1)%5] = 1; // ho 4 possibilità: // 1 - sia il filosofo a dx che quello a sx sono affamati e ci sono le
7 bacchette // 2 - il filosofo a dx è affamato e ci sono le bacchette // 3 - il filosofo a sx è affamato e ci sono le bacchette // 4 - nessuno dei 2 può mangiare if (b_libera[(i-1)%5] == 1 && sospeso[(i-1)%5] == 1) { sem_filosofo[(i-1)%5].v(); // PASSAGGIO DEL TESTIMONE if (b_libera[(i+2)%5] == 1 && sospeso[(i+1)%5) { sem_filosofo[(i+1)%5].v(); // PASSAGGIO DEL TESTIMONE else { else if (b_libera[(i+2)%5 == 1 && sospeso[(i+1)%5) { sem_filosofo[(i+1)%5].v(); // PASSAGGIO DEL TESTIMONE // start_nb: è necessario questo codice sotto? if (b_libera[(i-1)%5] == 1 && sospeso[(i-1)%5) { sem_filosofo[(i-1)%5].v(); // PASSAGGIO DEL TESTIMONE else { // end_nb: prendendo e rilasciando la mutex, forse sono più fair // nel (ri)controllare se svegliare il filosofo (i+1) else { Soluzione alternativa ai filosofi a cena II #define N 5 /* Numero di filosofi */ #define RIGHT(i) (((i)+1) % N) #define LEFT(i) (((i)==n)? 0 : (i)+1) // questa soluzione non usa l'idea di bacchette, ma setta lo stato dei filosofi typedef enum { THINKING, HUNGRY, EATING stato_filosofo; stato_filosofo state[n]; Semaphore sem_filosofo[n]; /* uno per filosofo, inizializzati a 0 */ /* Questa è la funzione principale, dato un filosofo "i", controlla quello che sta facendo il filosofo a destra e a sinistra. Il trucco qui è che se verifica che "i" può mangiare, allora setta il suo stato a EATING e chiama una V() sul semaforo privato di "i". La cosa è un po' oscura, nel senso che troviamo qui una V() senza una P() nella stessa porzione di codice (la relativa P() la troviamo in get_forks()). */ void test(int i) { if ( state[i] == HUNGRY && state[left(i)]!= EATING && state[right(i)]!= EATING ) { state[i] = EATING; sem_filosofo[i].v();
8 void get_forks(int i) { state[i] = HUNGRY; test(i); // se la condizione in test(i) precedente è stata verificata, allora // è stata preventivamente eseguita una sem_filosofo[i].v();, per cui // i non si bloccherà nella P() qui sotto // in caso contrario, il processo si blocca e potrà essere risvegliato // durante l'esecuzione di put_forks() da parte di un filofoso vicino sem_filosofo[i].p(); void put_forks(int i) { state[i]= THINKING; test(left(i)); test(right(i)); void philosopher(int process) { while(1) { think(); get_forks(process); eat(); put_forks(process); /* Nelle versioni precedenti un filosofo può cambiare lui lo stato del suo fiolosofo vicino. Se la cosa non ci piace, dobbiamo creare un'altra versione, in cui ogni volta che un filosofo rilascia le bacchette, sveglia il filosofo accanto, che dovrà controllare se ha la possibilità di prendere 2 bacchette. Questa possibilità non è sempre verificata, per cui il filosofo potrebbe essere stato svegliato per niente. */ Soluzione alternativa ai filosofi a cena III (sub-ottimale, ma che consente l'incapsulamento dello stato dei filosofi) void get_forks(int i) { state[i] = HUNGRY; while ( state[i] == HUNGRY ) { // controllo se posso mangiare if ( state[i] == HUNGRY && state[left]!= EATING && state[right(i)]!= EATING ) { state[i] = EATING; sem_filosofo[i].v(); // grazie a questa V() non mi bloccherò nella P() successiva sem_filosofo[i].p(); // se sto mangiando (entrato nell'if precedente) non mi blocco,
9 // altrimenti mi blocco e aspetto che mi risvegli un vicino void put_forks(int i) { state[i]= THINKING; if ( state[left(i)] == HUNGRY ) sem_filosofo[left(i)].v(); // lo sveglio affichè controlli se puà mangiare if ( state[right(i)] == HUNGRY) sem_filosofo[right(i)].v(); // lo sveglio affichè controlli se puà mangiare void philosopher(int process) { while(1) { think(); get_forks(process); eat(); put_forks(); Problema: i processi debbono risvegliarsi in ordine non FIFO Soluzione: invece di usare un unico semaforo S su cui tutti si sospendono, ogni processo ha un proprio semaforo S[i], sul quale si sospende facendo S[i].P(). Chi lo vuole risvegliare lo fa con S[j].V(). Problema: da dove ricava j? Idea: quando il processo "i" si sospende prima pubblica il proprio pid() i in una qualche struttura dati. Il processo che vuole risvegliare pesca da questa struttura dati con una politica!= FIFO. Esempio: coda con priorita' (2) bool libero = 1; queue coda[n]; // N e' il numero di priorita' Semaphore S[M]; // M e' il numero di processi nel sistema Process P[i,p] { // processo di indice i con priorita' p if (!libero) { queue[p].push(i); S[i].P(); else {.. // viene servito int k = 0; int svegliato = 0; while (!svegliato && k<n) { if (empty(queue[k])) k++;
10 else { // c'è qualcuno da riattivare j = queue[k].pop(); svegliato = 1; S[j].V();
Sincronizzazione. Problemi di sincronizzazione tipici Stefano Quer Dipartimento di Automatica e Informatica Politecnico di Torino
Sincronizzazione Problemi di sincronizzazione tipici Stefano Quer Dipartimento di Automatica e Informatica Politecnico di Torino 2 Produttore-Consumatore Produttore e consumatore con memoria limitata Utilizza
Sistemi operativi - Concetti ed esempi -Settima edizione
Capitolo 6: Sincronizzazione dei processi Capitolo 6: Sincronizzazione dei processi Introduzione Problema della sezione critica Soluzione di Peterson Hardware per la sincronizzazione Semafori Problemi
ESERCIZIO SincrAmbGlob-1
ESERCIZI DI SINCRONIZZAZIONE TRA THREAD CON SEMAFORI ESERCIZIO SincrAmbGlob-1 Si consideri un sistema nel quale è definito il semaforo sem1 e i thread P1, P2 e P3. Al tempo t il semaforo sem1 ha la seguente
Sistemi operativi. Lez. 9: primitive per la concorrenza i semafori
Sistemi operativi Lez. 9: primitive per la concorrenza i semafori Disabilitazione interrupt Due processi possono trovarsi in sezione critica simultaneamente solo perché chi vi è entrato per primo è stato
Esercizi di utilizzo del semaforo semplice di competizione per l'uso di una risorsa comune
Esercizi di utilizzo del semaforo semplice di competizione per l'uso di una risorsa comune a) Uso di una risorsa condivisa Siano P 1, P 2, P 3,, P k i processi che condividono l uso di una risorsa comune
Problema dei Fumatori di sigarette. Problema dei Fumatori di sigarette. Problema dei Fumatori di sigarette
1 3 processi fumatori 1 processo tabaccaio Ogni fumatore esegue ripetutamente un ciclo in cui prepara una sigaretta e poi la fuma Per preparare la sigaretta un fumatore ha bisogno di 3 ingredienti (risorse):
Algoritmo di Dekker. Algoritmo di Peterson
shared int turn = P; shared boolean needp = false; shared boolean needq = false; cobegin P // Q coend Riassunto Utile Concorrenza Algoritmo di Dekker process P { needp = true; while (needq) if (turn ==
Lettori e scrittori, Barbiere sonnolento
DTI / ISIN / Titolo principale della presentazione 1 Lettori e scrittori, Barbiere sonnolento Amos Brocco, Ricercatore, DTI / ISIN 14 maggio 2012 Lettori e scrittori Problema che riproduce una situazione
Monitor [Hoare 74] Costrutto sintattico che associa un insieme di procedure/funzioni (entry) ad una struttura dati comune a più processi.
Monitor [Hoare 74] Costrutto sintattico che associa un insieme di procedure/funzioni (entry) ad una struttura dati comune a più processi. Il compilatore può verificare che esse siano le sole operazioni
Hilzer s Barbershop problem
The Little Book of Semaphores by Allen B. Downey http://greenteapress.com/semaphores/ Versione più complicata del classico problema del barbiere 1 Il nostro barbershop ha tre sedie, tre barbieri, e un'area
Esercitazione 15. Il problema dello Sleeping Barber
Università degli Studi della Calabria Corso di Laurea in Ingegneria Informatica A.A. 2001/2002 Sistemi Operativi Corsi A e B Esercitazione 15 Il problema dello Sleeping Barber E dato un salone di barbiere,
Sistemi Operativi. Lezione 7-bis Esercizi
Sistemi Operativi Lezione 7-bis Esercizi Esercizio Problema dei lettori e scrittori Un insieme di processi condivide un file dal quale alcuni possono solo leggere i dati, altri solo scriverli Più lettori
SEMAFORI SEMAFORI. Sul semaforo sono ammesse solo due operazioni (primitive)
SEMAFORI 1 SEMAFORI Variabile intera non negativa con valore iniziale >= 0 Al semaforo è associata una lista di attesa Qs nella quale sono posti i descrittori dei processi che attono l autorizzazione a
Problematiche dello scambio di messaggi Produttore-consumatore con scambio di messaggi
Scambio di messaggi Comunicazione non basato su memoria condivisa con controllo di accesso. Meccanismi di sincronizzazione Scambio di messaggi Basato su due primitive (chiamate di sistema o funzioni di
Algoritmi di Ricerca. Esempi di programmi Java
Fondamenti di Informatica Algoritmi di Ricerca Esempi di programmi Java Fondamenti di Informatica - D. Talia - UNICAL 1 Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare
5 Thread. 5 Thread. 5 Thread. Ad un generico processo, sono associati, in maniera univoca, i seguenti dati e le seguenti informazioni:
1 Ad un generico processo, sono associati, in maniera univoca, i seguenti dati e le seguenti informazioni: codice del programma in esecuzione un area di memoria contenente le strutture dati dichiarate
SISTEMI OPERATIVI. Semafori. Semafori
SISTEMI OPERATIVI 04.a Semafori Semafori Approfondimento sulla mutua escluone Manipolazione di risorse mutex Strategie di soluzione Lock e Un Primitive semaforiche 1 Approfondimento sulla mutua escluone
= PTHREAD_MUTEX_INITIALIZER
MUTEX Prof.ssa Sara Michelangeli Quando si programma in modalità concorrente bisogna gestire le problematiche di accesso alle eventuali sezioni critiche. Una sezione critica è una sequenza di istruzioni
SOLUZIONE. ESERCIZIO PRODUTTORE CONSUMATORE (semafori)
ESERCIZIO PRODUTTORE CONSUMATORE (semafori) In un sistema operativo, i thread A (produttore) e B (consumatore) cooperano scambiandosi messaggi attraverso un buffer di 10 celle, ciascuna capace di contenere
LE STRUTTURE DATI DINAMICHE: GLI ALBERI. Cosimo Laneve
LE STRUTTURE DATI DINAMICHE: GLI ALBERI Cosimo Laneve 1 argomenti 1. definizione di alberi e nozioni relative 2. implementazione degli alberi, creazione, visita 3. algoritmo di visita iterativa e sua implementazione
Java Virtual Machine. Indipendenza di java dalla macchina ospite. I threads in Java
programmi sorgente: files.java compilatore Indipendenza di java dalla macchina ospite Programmi java Programmi java Programmi java files.class bytecode linker/loader bytecode bytecode Java API files.class
Realizzazione di Politiche di Gestione delle Risorse: i Semafori Privati
Realizzazione di Politiche di Gestione delle Risorse: i Semafori Privati Condizione di sincronizzazione Qualora si voglia realizzare una determinata politica di gestione delle risorse,la decisione se ad
Soluzioni ai problemi di Mutua Esclusione Primitive di sincronizzazione. Soluzioni ai problemi di Mutua EsclusionePrimitive di sincronizzazione
Soluzioni ai problemi di Mutua Esclusione Primitive di sincronizzazione Soluzioni basate su primitive di sincronizzazione Le primitive di sincronizzazione piú comuni sono: Lock (mutex) - realizzati in
6 - Blocchi e cicli. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo
6 - Blocchi e cicli Programmazione e analisi di dati Modulo A: Programmazione in Java Paolo Milazzo Dipartimento di Informatica, Università di Pisa http://pages.di.unipi.it/milazzo milazzo di.unipi.it
Il monitor. Costrutti linguistici per la sincronizzazione
Il monitor 1 Costrutti linguistici per la sincronizzazione I semafori costituiscono un meccanismo molto potente per la sincronizzazione dei processi. Tuttavia, il suo uso può risultare troppo a basso livello.
UNIVERSITÀ DEGLI STUDI DI BERGAMO
UNIVERSITÀ DEGLI STUDI DI BERGAMO DIPARTIMENTO DI INGEGNERIA DELL INFORMAZIONE E METODI MATEMATICI Corso di Laurea in Ingegneria Informatica/Meccanica Esame di Sistemi Operativi a.a. 2008-09 (Modulo di
Algoritmi di Ricerca. Esempi di programmi Java
Fondamenti di Informatica Algoritmi di Ricerca Esempi di programmi Java Fondamenti di Informatica - D. Talia - UNICAL 1 Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare
Lezione 6 Introduzione al C++ Mauro Piccolo
Lezione 6 Introduzione al C++ Mauro Piccolo [email protected] Linguaggi di programmazione Un linguaggio formale disegnato per descrivere la computazione Linguaggi ad alto livello C, C++, Pascal, Java,
Decima Esercitazione. Accesso a risorse condivise tramite Monitor Java
Decima Esercitazione Accesso a risorse condivise tramite Monitor Java 1 Agenda Esempio 1 La banca: gestione di una risorsa condivisa da più thread, con politica prioritaria Esercizio 2 da svolgere Accesso
Interazione tra Processi
Interazione tra Processi 1 Classificazione: Processi interagenti processi interagenti/indipendenti: due processi sono indipendenti se l esecuzione di ognuno non è in alcun modo influenzata dall altro.
Cenni ai Problemi classici di Sincronizzazione Processi Concorrenti. E. Mumolo
Cenni ai Problemi classici di Sincronizzazione Processi Concorrenti E. Mumolo Problemi classici Problemi di mutua esclusione proposti nel corso degli anni Modelli di problemi reali Problema del produttore
Architettura degli Elaboratori 2
Architettura degli Elaboratori 2 Esercitazioni 2 Grafo di allocazione delle risorse Comunicazione tra processi A. Memo - 2004 Grafo di allocazione delle risorse è uno strumento grafico utile per l individuazione
Esercizio sul Monitor. Ponte con utenti grassi e magri 11 Novembre 2013
Esercizio sul Monitor Ponte con utenti grassi e magri 11 Novembre 2013 1 Ponte con utenti grassi e magri Si consideri un ponte pedonale che collega le due rive di un fiume. Al ponte possono accedere due
Java SE 7. Strutture di programmazione (2)
Java SE 7 Strutture di programmazione (2) [email protected] [email protected] Casting (1) E' l'operazione che realizza la conversione di tipo tra oggetti e tra tipi primitivi. E' un'operazione
Le strutture di controllo in C++
Le strutture di controllo in C++ Docente: Ing. Edoardo Fusella Dipartimento di Ingegneria Elettrica e Tecnologie dell Informazione Via Claudio 21, 4 piano laboratorio SECLAB Università degli Studi di Napoli
Processi Interagenti L interazione puo` avvenire mediante due meccanismi: Comunicazione: scambio di informazioni tra i processi interagenti.
Interazione tra Processi Processi Interagenti L interazione puo` avvenire mediante due meccanismi: Comunicazione: scambio di informazioni tra i processi interagenti. P1 Sincronizzazione: imposizione di
Fondamenti di informatica T-1 (A K) Esercitazione 2 Basi del linguaggio Java
Fondamenti di informatica T-1 (A K) Esercitazione 2 Basi del linguaggio Java AA 2018/2019 Tutor Lorenzo Rosa [email protected] 2 Programma Introduzione al calcolatore e Java Linguaggio Java, basi e
Il costrutto monitor [Hoare 74]
Il monitor 1 Il costrutto monitor [Hoare 74] Definizione: Costrutto sintattico che associa un insieme di operazioni (entry o public) ad una struttura dati comune a più processi, tale che: Le operazioni
Il costrutto monitor [Hoare 74]
Il monitor 1 Il costrutto monitor [Hoare 74] Definizione: Costrutto sintattico che associa un insieme di operazioni (entry, o public) ad una struttura dati comune a più processi, tale che: Le operazioni
7 - Programmazione procedurale: Dichiarazione e chiamata di metodi ausiliari
7 - Programmazione procedurale: Dichiarazione e chiamata di metodi ausiliari Programmazione e analisi di dati Modulo A: Programmazione in Java Paolo Milazzo Dipartimento di Informatica, Università di Pisa
Sistemi Operativi Esercizi Ricapitolazione. Docente: Claudio E. Palazzi
Sistemi Operativi Esercizi Ricapitolazione Docente: Claudio E. Palazzi [email protected] Problema Numerosi operai in una fabbrica preparano un unico prodotto con l utilizzo di na quantità del componente
