Visualizzazione dei risultati da 1 a 5 su 5
  1. #1

    Esercizio Produttore-Consumatore

    Ragazzi ho problemi con un semplice esercizio Produttore-Consumatore, "applicato" ad un pronto soccorso.

    E' il primo esercizio su questo argomento e quindi è molto molto semplice; come struttura dati ho usato una LinkedList gestita come una coda, quindi l'ultimo arrivato viene accodato ed il primo viene prelevato dalla testa(non usato i vari codici e quindi code di priorità, ma tutti i pazienti hanno lo stesso codice).

    La dimensione della coda la posso scegliere dal main.

    C'è la classe Paziente:
    codice:
    public class Paziente{
    	
    	private String nome;
    	private String cognome;
    	
    	
    	public Paziente(String nome, String cognome)
    	{
    		this.nome=nome;
    		this.cognome=cognome;
    	}
    
    	
    	public String toString ()
    	{
    		return ("Cognome: " + cognome + "\n" + "Nome: " + nome + "\n"); 
    	}
    
    
    	}

    Poi c'è la classe Buffer:
    codice:
    import java.util.LinkedList;
    
    
    public class Buffer<E> {
    	
    	private LinkedList<E> coda;
    	private int capacità;
    	
    	
    	public Buffer(int capacità)
    	{
    		this.capacità=capacità;
    		coda=new LinkedList<E>();
    	}
    	
    	
    	public synchronized boolean isEmpty()
    	{
    		return (coda.size()==0);
    	}
    	
    	public synchronized boolean isFull()
    	{
    		return (coda.size()>=capacità);
    	}
    	
    	
    	public synchronized void aggiungi(E elem)
    	{
    		while(isFull())
    		{
    			try
    			{
    				wait();
    			}
    			catch(InterruptedException e){}
    		}
    		
    		coda.addLast(elem);
    		
    		notifyAll();
    	}
    	
    	
    	public synchronized E preleva ()
    	{
    		while(isEmpty())
    		{
    			try
    			{
    				wait();
    			}
    			catch(InterruptedException e){}
    		}
    		
    		notifyAll();
    		
    		return (coda.removeFirst());
    		
    		}
    
    }
    Poi c'è la classe Consumatore:
    codice:
    public class Consumatore implements Runnable {
    
    	private Buffer<Paziente> buffer;
    	private int ritardo;
    	
    	
    	public Consumatore (Buffer<Paziente> buffer, int ritardo)
    	{
    		this.ritardo=ritardo;
    		this.buffer=buffer;
    	}
    	
    	public void run() {
    		
    		int i;
    		
    		while(true)
    		{
    			Paziente p = buffer.preleva();
    			System.out.println ("Prelevato: " + p + "\n" );
    			
    			try
    			{
    				Thread.sleep(ritardo);
    			}
    			catch(InterruptedException e){}
    			
    		}
    		
    	}
    
    }
    La classe Produttore:
    codice:
    
    public class Produttore implements Runnable{
    
    	private Buffer<Paziente> buffer;
    	private int ritardo;
    	
    	
    	public Produttore (Buffer<Paziente> buffer, int ritardo)
    	{
    		this.buffer=buffer;
    		this.ritardo=ritardo;
    	}
    	
    	
    	
    	public void run() {
    		
    		int i;
    		
    		for(i=0; true; i++)
    		{
    			Paziente p = new Paziente("Nome " + i, "Cognome " + i);
    			
    			buffer.aggiungi(p);
    			System.out.println("Inserito: " + p);
    			
    			try
    			{
    				Thread.sleep(ritardo);
    			}
    			catch(InterruptedException e){}
    			
    			Thread.yield();
    		}		
    	}
    
    }

    E per finire ho scritto un semplice Test:
    codice:
    public class TestProntoSoccorsoCoda {
    
    	
    	public static void main(String[] args) {
    		
    		Buffer<Paziente> buffer;
    		Produttore prod;
    		Consumatore cons;
    		
    		buffer=new Buffer<Paziente>(1);
    		prod=new Produttore(buffer, 1000);
    		cons=new Consumatore(buffer, 1000);
    		
    		
    		Thread consumator=new Thread(cons);
    		Thread producer=new Thread(prod);
    		
    		
    		producer.start();
    		consumator.start();
    		
    	}
    }

    Dal main posso scegliere la velocità di produzione e la velocità di consumo, ed anche la dimensione del buffer.

    Il problema che si verifica è che sulla console ci sono dei Pazienti curati prima che vengano inseriti nel buffer, oppure vengono effettuati più inserimenti successivi anche se la dimensione non lo permetterebbe.

    Per esempio se scelgo dimensione 1, allora si dovrà avere un'alternanza continua tra curati e inseriti, ossia non ci possono essere due inseriti o due curati consecutivi proprio perché la struttura dati ha dimensione fisica massima pari ad uno; ma effettuando il test dalla console risulta che alcune volte ci sono due curati consecutivi, però il bello è che cura il malato che verrà inserito subito dopo.

    Ad esempio arriva il paziente1 e poi cura il paziente1, poi cura il paziente2 e poi arriva il paziente2; ma come ma fa a curarlo se ancora non è arrivato?

    Io sono arrivato alla conclusione che si tratti di un problema di console o di stampa, cioè il paziente viene inserito nella struttura dati ma sulla console viene stampato prima il messaggio di cura che di inserimento (per qualche ritardo della console o non sò).

    Un problema di struttura non c'è perchè anche se effettua due inserimento successivi, ad esempio del paziente n e del paziente n+1, poichè la dim massima è 1, un paziente dovrebbe andar perso, ma così non è perchè successivamente vengono curati entrambi.


    Scusate se mi sono dilungato, ma volevo essere chiaro e far capire il problema.
    Confido in un vostro aiuto
    Grazie

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284

    Re: Esercizio Produttore-Consumatore

    Originariamente inviato da vincyilbiondo
    Il problema che si verifica è che sulla console ci sono dei Pazienti curati prima che vengano inseriti nel buffer, oppure vengono effettuati più inserimenti successivi anche se la dimensione non lo permetterebbe.

    Per esempio se scelgo dimensione 1, allora si dovrà avere un'alternanza continua tra curati e inseriti, ossia non ci possono essere due inseriti o due curati consecutivi proprio perché la struttura dati ha dimensione fisica massima pari ad uno; ma effettuando il test dalla console risulta che alcune volte ci sono due curati consecutivi, però il bello è che cura il malato che verrà inserito subito dopo.
    Premetto che non ho letto proprio tutto il codice. Comunque il "fulcro" di tutto il programma è la classe Buffer e ad una prima occhiata direi che è corretta e appropriata.

    I problemi che descrivi sono a livello di visualizzazione sulla console.

    buffer.aggiungi(p);
    System.out.println("Inserito: " + p);

    e

    Paziente p = buffer.preleva();
    System.out.println ("Prelevato: " + p + "\n" );

    Tieni presente una cosa. Quello che è certo è la sincronizzazione che esiste tra il aggiungi e il preleva. Ma per il resto le tempistiche sono aleatorie, dipendono da molte cose e variabili dipendenti dal sistema e dallo scheduler dei thread.

    In sostanza: è perfettamente possibile che il thread consumatore arrivi ad eseguire il println di "Prelevato xxx" prima del momento in cui il thread produttore arrivi ad eseguire il println di "Inserito xxx".
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Grazie andbin per avermi risposto e chiarito il dubbio.

    Sono stato due giorni su questo esercizio, usando di tutto e di più, LinkedList ArrayBlockinkQueue PriorityQueue ecc.. (quindi strutture dati già sincronizzate e non) ma si verificavano sempre questi "misteriori" output e alla fine ho ipotizzato che si potesse trattare di problemi di tempificazione della console ecc...

    Quindi ora ho capito che anche se gli accessi alla struttura dati condivisa sono sincronizzati e gestiti in mutua esclusione, una volta terminate queste operazioni, non è garantito che l'output venga gestito in modo sincrono; ma a questo punto vorrei farti una domanda: è possibile rendere sincronizzati anche le stampe degli output e non solo gli accessi alla struttura dati?

    Grazie 1000

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da vincyilbiondo
    una volta terminate queste operazioni, non è garantito che l'output venga gestito in modo sincrono;
    No appunto. Ci sarebbe tutta una spiegazione teorica dietro ma te la risparmio (anche per me, vista l'ora).

    Originariamente inviato da vincyilbiondo
    ma a questo punto vorrei farti una domanda: è possibile rendere sincronizzati anche le stampe degli output e non solo gli accessi alla struttura dati?
    Devi fare in modo che le sequenze aggiungi+println e l'altra preleva+println siano in mutua esclusione.

    Cioè aggiungi+println deve essere una singola unità non divisibile in mutua esclusione a preleva+println come altra singola unità non divisibile.
    Ti basta racchiudere le due sequenze in blocchi synchronized in modo che entrambi i synchronized usino lo stesso lock (cioè sullo stesso oggetto).
    Questo oggetto potrebbe essere un qualunque altro, anche un banale nuovo oggetto con new Object() che sia però, ovviamente, condiviso. Ma visto che tra i due thread è già condiviso il Buffer ... usa quello.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  5. #5
    Andbin ho aggiunto all'interno dei metodi run() del Produttore e del Consumatore due blocchi synchronized, sincronizzati sull'oggetto buffer, e sembra che ora funzioni tutto bene


    codice:
    public class Consumatore implements Runnable {
    
    	private Buffer<Paziente> buffer;
    	private int ritardo;
    	
    	
    	public Consumatore (Buffer<Paziente> buffer, int ritardo)
    	{
    		this.ritardo=ritardo;
    		this.buffer=buffer;
    	}
    	
    	public void run() {
    		
    		int i;
    		
    		for(i=0; true; i++)
    		{
    			synchronized(buffer)
    			{
    			Paziente p = buffer.preleva();
    			System.out.println ("Prelevato: " + p + "\n" + buffer.getSize());
    			}
    			try
    			{
    				Thread.sleep(ritardo);
    			}
    			catch(InterruptedException e){}	
    		}	
    	}
    }


    codice:
    public class Produttore implements Runnable{
    
    	private Buffer<Paziente> buffer;
    	private int ritardo;
    	
    	
    	public Produttore (Buffer<Paziente> buffer, int ritardo)
    	{
    		this.buffer=buffer;
    		this.ritardo=ritardo;
    	}
    	
    	
    	
    	public void run() {
    		
    		int i;
    		
    		for(i=0; true; i++)
    		{
    			synchronized(buffer)
    			{
    			Paziente p = new Paziente("Nome " + i, "Cognome " + i);
    			
    			buffer.aggiungi(p);
    			System.out.println("Inserito: " + p + buffer.getSize());
    			}
    			try
    			{
    				Thread.sleep(ritardo);
    			}
    			catch(InterruptedException e){}	
    		}	
    	}
    }
    Ho provato tutte le possibili combinazioni, velocità uguali del prod. e cons., diverse ecc..... e con qualsiasi dimensione della struttura dati, e l'output è come ci si aspettasse, quindi non ci sono più problemi di stampe di pazienti prima delle stampe dell'arrivo, e/o viceversa.

    Grazie 1000 Andbin

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.