Programmazione concorrente e semafori

di il
6 risposte

Programmazione concorrente e semafori

Ciao a tutti sto impazzendo su questo progetto per l'uni. teoricamente è gia pronto se non fosse per un solo dei molteplici punti che lo compongono e mi sta facexndo sfasare di brutto.
vi posto le specifiche.

Il progetto è composto da 4 parti da svolgere un stretta sequenza (la parte in rosso è quella incriminata):

Parte prima ?
-Implementare la classe FairSemaphore che definisce un semaforo generale assicurando che la gestione dei thread sospesi sul semaforo sia rigorosamente First-In-First-Out.
-Per l’implementazione della classe FairSemaphore devono essere utilizzati esclusivamente i meccanismi di sincronizzazione offerti da Java versione 4.0 (blocchi e/o metodi synchronized, metodi wait(), notify() e notifyll() ) .

Parte seconda ?
- Utilizzando come meccanismo di sincronizzazione esclusivamente i semafori (come implementati nella parte prima), implementare la classe SynchPort che simula una porta sincronizzata.
- La porta deve essere generica rispetto al tipo T di informazione scambiata (class SynchPort<T>)
- Ogni porta deve essere dichiarata come oggetto public locale al thraed (processo) ricevente. La porta deve essere infatti visibile a ciascuno dei processi mittenti che dovranno invocare il metodo send sulla specifica porta.
- Il numero di mittenti deve essere passato come parametro al costruttore della porta.
-Il metodo receive deve restituire al processo ricevente l’informazione di tipo <T>contenuta nel messaggio ricevuto e l’identificazione del processo mittente.

Parte terza
-Implementare la classe PortVector che definisce un array di porte sincronizzate da usare poi, nella parte quarta, come array di porte attraverso le quali un processo server riceve richieste di servizio. Per questo, locale ad un processo server verrà dichiarato un oggetto (public) di tipo PortVector ?
-Il numero N delle porte componenti il vettore è passato come parametro al costruttore della classe.?
-Per semplicità, tutte le porte del vettore vengono definite con lo stesso numero di processi mittenti (il numero dei clienti del server).?
-Ogni processo mittente (cliente) invia i propri messaggi ad una delle porte dell’array identificata, oltre che dall’identificatore dell’oggetto di tipo PortVector anche dal suo indice nell’array stesso. .?
-Il processo ricevente (server) utilizza, per ricevere messaggi, il metodo Receive a cui vengono passati come parametri, fra l’altro, un vettore di interi vet e un intero n.
n rappresenta la dimensione del vettore vet (1>=n>=N). Gli elementi di vet sono n interi che rappresentano gli indici delle porte (indici di elementi del vettore PortVector) da cui si vuol ricevere.
La receive è bloccante se su nessuna delle porte indicate esiste un mittente pronto a inviare un messaggio. In questo caso il processo ricevente verrà svegliato quando sarà possibile ricevere da una qualunque delle porte indicate.
Se, viceversa, da una o da più di una delle porte è possibile ricevere, ne viene scelta una a piacere e viene ricevuto il messaggio da tale porta.
Di nuovo, la receive deve restituire al processo ricevente l’informazione di tipo <T> contenuta nel messaggio ricevuto e l’identificazione del processo mittente, ma in questo caso, deve essere restituito anche l’indice della porta dell’array dalla quale il messaggio è stato ricevuto.

Parte quarta la ometto

e qui posto il codice
import java.util.Vector;


public class portvector<T> {

	Vector<Synchport<T>> porte;
	int n_mess[]; 
	FairSemaphore Rcv, mutex;//deve in qualche modo diventare un semaforo binario
	int porteAttese[];
	boolean firstAccess;
	
	
	
	
	//costruttore della classe
	


	public portvector(int n_m, int n_p) { 
		porte= new Vector<Synchport<T>>(n_p);
		n_mess=new int[n_p];
		
		firstAccess=false;
		porteAttese= new int [n_p];
		for (int j=0;j<n_p;j++)
		{
			porte.add(new Synchport<T>(n_m));
			n_mess[j]=0;
			porteAttese[j]=0;
			
		}
			
			
			
		Rcv=new FairSemaphore("Rcv", 0);
		mutex=new FairSemaphore("mutex",1);
		
	}//fine costruttore


		public PortData<T> receive(int vet[], int n) throws InterruptedException // non funziona ancora
	{	 /// qui uso la tecnica del passaggio del testimone
		mutex.P();
		
		firstAccess=true;
		for ( int i = 0; i<n;i++)
			porteAttese[vet[i]]=porteAttese[vet[i]]+1;//segna preventivamente su quali porte il ricevitore vuole ricevere un messaggio
			
	while (true)
	{		
	for (int i = 0; i<n;i++)
		{			
		if (n_mess[vet[i]]>0)//vuol dire che la porta di indice vet[i] contiene almeno un mess
			{
			n_mess[vet[i]]=n_mess[vet[i]]-1;
		for (int j = 0; j<n;j++)
				{porteAttese[vet[j]]=porteAttese[vet[j]]-1;//elimina l'attesa preventiva dalle porte in quanto ha ricevuto il mess atteso
		
				}
			//System.err.print("\n");
			PortData<T> temp=porte.elementAt(vet[i]).receive();
			firstAccess=false;
					mutex.V();
			return temp;
			}
	
		}//fine for
		
		if (firstAccess)// implementa la tecnica del passaggio del testimone.
			{
			firstAccess=false;
			mutex.V();
			}
		else
			Rcv.V();
			
			
		
		
		Rcv.P();//non ci sono messaggi nelle porte specificate dunque il processo ricevente si blocca sul semaforo Rcv
		
		//mutex.P();
		}// fine while
	
	}//fine receive
	
	
	public void send(int i,PortData<T> p) throws InterruptedException {
		mutex.P();
		n_mess[i]=n_mess[i]+1;//indica che sulla porta i-esima è presente un altro messaggio
		
		
		
		if (porteAttese[i]>0)
			Rcv.V();//avverte il ricevente che un nuovo messaggio è presente nella porta;
		
		
		mutex.V();//rilascia il lock sull oggetto
		
		porte.elementAt(i).send(p);
		
		
	}//fine send

}//fine class portvector

e due parole sul funzionamento
All'interno della classe portvector troviamo.

• Un vettore di porte sincronizzate cui i vari client inviano messaggi.
• Un semaforo di mutua esclusione per l'accesso esclusivo all'oggetto portvector.
• Un semaforo Rcv in cui i lettori possano mettersi in attesa di messaggi.
• Un array di interi porteAttese: questo array indica le porte da cui il ricevitore desidera ricevere messaggi in un determinato istante
• Un vettore n_mess con dimensione pari a quella del vettore delle porte, che indica, alla posizione j, il numero di messaggi non letti all'interno della j-esima porta.
• Il buleano firstAccess: il suo ruolo sarà più chiaro nella spiegazione della Receive.



Il meccanismo della send è molto più semplice rispetto alla receive. Infatti, il mittente deve "solo" inviare l'informazione ,sulla porta di indice "i", incrementando il contatore dei messaggi (n_mess) associato alla porta, nel caso in cui tale porta fosse desiderata da un ricevitore deve effettuare una V() sul semaforo Rcv senza rilasciare la mutua esclusione, la quale verrà gestita da l thread lettore(tecnica del passaggio del testimone) in modo da risvegliare il ricevitore avvertendolo della presenza di un nuovo mesaggio, in caso contrario rilascia semplicemente la mutua esclusione.

Come indicato dal testo, il metodo receive() deve essere bloccante, vale a dire, se il ricevitore non trova messaggi in nessuna delle porte da cui ha deciso di ricevere, allora dovrà mettersi in attesa dell’arrivo di un messaggio da una qualunque delle porte indicate nel vettore vet.

un ricevitore che voglia ricevere da un set di porte, invocando la receive() dopo aver preso la mutua esclusione sull'oggetto, eseguirà una fase preliminare incrementando di 1 il contatore associato alle porte da cui aspetta un messaggio, questo viene logicamente fatto al difuori del ciclo while perché altrimenti ad ogni iterazione del ciclo tale fase porterebbe a risultati falsati.
Adesso il thread entra nel ciclo infinito del while.
Il thread a questo punto controllerà se sulle porte desiderate esistono dei messaggi.
Se sono presenti messaggi, su una qualunque delle porte desiderate, allora invocherà la receive propria di tale porta e ritornando il pacchetto di informazioni uscirà dal ciclo, naturalmente non prima di aver decrementato i contatori associati alle porte desiderate (opposta alla fase di attesa preventiva) e aver rilasciato la mutua esclusione sull'oggetto.
Se invece non sono presenti messaggi sulle porte desiderate (indicate dal vettore vet) allora il ricevitore essendo al primo si metterà in attesa sul semaforo Rcv.
Nel momento in cui uno dei mittenti invia un messaggio su una delle porte attese allora il ricevitore in attesa svegliatosi rientra nel ciclo, riesegue la fase di controllo e se trova un messaggio sulla porta desiderata allora esce dal metodo receive e rilascia la mutua esclusione. Se invece non ne trova questa volta sveglierà un altro ricevitore e si rimetterà in attesa sul semaforo RcV. La mutua esclusione non viene rilasciata e tale compito viene subordinato al thread appena svegliato implementando come nel caso della send la “tecnica del passaggio del testimone”.
In questa fase si ha la certezza che almeno uno dei ricevitori leggerà un messaggio dato che il mittente effettua una P() sul semaforo RcV se e solo se almeno un ricevitore è in attesa sulla porta da lui usata.
alla fine del while si fa la P() sul semaforo binario mutex, questo per garantire la mutua nell'unico caso in cui due thread operino sull'oggetto in contemporanea. infatti senza la P(mutex) se un thread accedesse all'oggetto e un thread bloccato si risvegliasse avremmo due thread che contemporaneamente agiscono sull'oggetto condiviso (il portvector)


i semafori usati sono di tipo FIFO e funzionano (testati in molti modi), come le porte sincronizzate(Synchport). all'interno delle porte sincronizzate ci sono due metodi send e receive. nella send un mittente rimane bloccato su un semaforo interno alla porta finchè il ricevente non estre il messaggio attraverso il metodo receive. analogamente con la receive un ricevente che lo usa si metterà in attesa su un altro semaforo interno se non sono presenti messaggi, in pratica uso la tecnica del rendez vous.
è tanta roba lo so e spero innanzitutto di esser stato chiaro e che voi siate in grado di aiutarmi. ne va della mia salute mentale.

il problema è che a un certo punto tutto si blocca e nessuno riesce ad andare avanti. come se pur essendoci messaggi in una qualche porta ilo ricevitore in attesa non riesce a vederlo

6 Risposte

  • Re: Programmazione concorrente e semafori

    Evvvai: quanto mi piacciono gli abbracci mortali.

    Hai scritto un papiro, ma l'unica informazione utile (visto che l'esercizio lo devi risolvere tu e non noi) e' la seguente:

    il problema è che a un certo punto tutto si blocca e nessuno riesce ad andare avanti. come se pur essendoci messaggi in una qualche porta ilo ricevitore in attesa non riesce a vederlo

    Sintomo classico dell'abbraccio mortale

    Che cosa e' un abbraccio mortale? -> Un deadlock!

    Come si risolvono i deadlock?

    E' per questo che la programmazione concorrente e' cosi' complicata -> sapere come evitare o risolvere i deadlock.

    In alterantiva, identificarli.

    Identificarli e' quasi (quasi per modo di dire) sempre un bagno di sangue.
    Nel caso di Java, comunque, c'e' almeno un IDE che ti assiste: IntelliJ Idea.
    In alternativa, implementi il meccanismo di identificazione -> non complicatissimo, ma sicuramente un bel progetto.

    Nei ritagli di tempo, fallo: ti capitera' di usarlo piu' di qualche se la tua vita professionale prevedera' l'utilizzo dei thread. Sono pochissimi i framework che mettono a disposzione strumenti per identificare i deadlock.

    Nel tuo caso, il miglior sistema, e' quello di non generarli.
    Come si fa?

    Ci sono due regole base:

    1) usare un unico oggetto di sincronizzazione
    2) con due o piu' oggetti, acquisire il lock SEMPRE NELLO STESSO ORDINE

    Regola aggiuntiva:

    x) delegare l'acquisizione dei lock ad un'unico oggetto, in modo da controllare come vengono acquisiti

    Condoglianze
  • Re: Programmazione concorrente e semafori

    Oltre alla simpatica e utile risposta di migliorabile io aggiungo alcune cose.

    Il deadlock, in generale, è una situazione di "stallo" (spesso permanente) in cui due (o più) entità sono bloccate nel proseguire il loro lavoro, perché ognuna di esse è in attesa di acquisire/usare una risorsa "esclusiva" che in quel momento è già in uso da una delle altre entità.

    Per farti capire il punto 2) indicato da migliorabile, questo è un codice che concettualmente può andare in deadlock e all'atto pratico ci va quasi sicuramente (dalle mie prove nessuno dei due thread è mai riuscito a terminare i 100 cicli!).
    public class ProvaDeadlock {
        public static void main(String[] args) {
            final Object lockObj1 = new Object();
            final Object lockObj2 = new Object();
    
            Runnable r1 = new Runnable() {
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        synchronized (lockObj1) {
                            synchronized (lockObj2) {
                                System.out.println("r1 ha lockObj1+lockObj2");
    
                                try {
                                    Thread.sleep(100);
                                } catch (Exception e) {}
                            }
                        }
                    }
                }
            };
    
            Runnable r2 = new Runnable() {
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        synchronized (lockObj2) {
                            synchronized (lockObj1) {
                                System.out.println("r2 ha lockObj1+lockObj2");
    
                                try {
                                    Thread.sleep(100);
                                } catch (Exception e) {}
                            }
                        }
                    }
                }
            };
    
            new Thread(r1).start();
            new Thread(r2).start();
        }
    }
    E tutto questo semplicemente perché i due thread acquisiscono i due lock in ordine differente.
  • Re: Programmazione concorrente e semafori

    Dunque secondo voi tutto dipende dal modo in cui si acquisisce la mutua esclusione sui semafori. effettivamente il ciclo era la parte più ostica. grazie mille dei suggerimenti, vi farò sapere.

    migliorabile ha scritto:


    In alternativa, implementi il meccanismo di identificazione -> non complicatissimo, ma sicuramente un bel progetto.

    Nei ritagli di tempo, fallo: ti capitera' di usarlo piu' di qualche se la tua vita professionale prevedera' l'utilizzo dei thread. Sono pochissimi i framework che mettono a disposzione strumenti per identificare i deadlock.

    cosa intendi per meccanismo di identificazione?
    e cosa dovrei fare nei ritagli di tempo?
  • Re: Programmazione concorrente e semafori

    atelzut ha scritto:


    cosa intendi per meccanismo di identificazione?
    e cosa dovrei fare nei ritagli di tempo?
    Non posso sapere a cosa si riferiva esattamente migliorabile ma potrei presumere che si riferiva ad una interfaccia del management nel framework (package java.lang.management, per monitorare/gestire la JVM) che si chiama ThreadMXBean.

    E qui c'è anche una discussione utile:
    http://stackoverflow.com/questions/1102359/programmatic-deadlock-detection-in-java
  • Re: Programmazione concorrente e semafori

    La domanda sorge spontanea: ma hai chiaro che cosa e' un deadlock?

    Perche' se chiedi che cosa e' un meccanismo di identificazione (di un deadlock), allora forse non ti e' chiaro quale e' il problema da affrontare e perche' va risolto in quel specifico modo (e non secondo noi ).

    Nella programmazione concorrente e distribuita, il concetto di deadlock e' fondamentale, non una semplice curiosita' per un esercizio di programmazione.
  • Re: Programmazione concorrente e semafori

    migliorabile ha scritto:


    La domanda sorge spontanea: ma hai chiaro che cosa e' un deadlock?

    Perche' se chiedi che cosa e' un meccanismo di identificazione (di un deadlock), allora forse non ti e' chiaro quale e' il problema da affrontare e perche' va risolto in quel specifico modo (e non secondo noi ).

    Nella programmazione concorrente e distribuita, il concetto di deadlock e' fondamentale, non una semplice curiosita' per un esercizio di programmazione.
    certo che so cosa è un deadlock solo non capivo a cosa ti riferissi con meccanismo di identificazione. tutto qui.
Devi accedere o registrarti per scrivere nel forum
6 risposte