View Full Version : [Java] Programmazione concorrente e thread
Ciao, sto esaurendo su un progetto per l'esame di programmazione concorrente spero di trovare qualcuno che mi possa chiarire qualche dubbio.
Per adesso solo una domandina rapida rapida:
E' possibile che un thread in un ciclo while(condizione) monopolizzi la CPU in modo da non permettere ad un altro thread di cambiare tale condizione facendo andare il programma in loop infinito?
Dal punto di vista di Java sì, non c'è una politica di fairness prestabilita.
Dal punto di vista di Java sì, non c'è una politica di fairness prestabilita.
Allora, la mia situazione è questa:
Devo implementare una receiveAny su un array di porte.
Ho un semaforo su cui il server si sospende se tutte le porte sono vuote (nessun mittente ha lasciato un messaggio).
Il server sospeso su questo semaforo viene svegliato dal mittente prima di aver depositato il messaggio. Purtroppo lo devo svegliare prima di aver effettivamente lasciato il messaggio perché le porte sono sincrone, cioè quando il mittente inserisce il messaggio si sospende in attesa che qualcuno faccia receive.
Per superare l'inconveniente che il server svegliato dal semaforo porteVuote non trovi alcun messaggio perché il mittente perde il controllo della CPU dopo aver segnalato il server ma prima di aver effettivamente inserito il messaggio ho fatto così:
private int testa_porte()throws InterruptedException{
int porta = -1;
for(int i=0; i<array.length; i++){
if(array[i].haveSospesi()){
porta = i;
break;
}
}
porteVuote.acquire();
while(porta == -1){
System.out.println("Cerco...");
for(int i=0; i<array.length; i++){
if(array[i].haveSospesi()){
porta = i;
break;
}
}
Thread.sleep(500);
}
return porta;
}
In pratica il server inizialmente cerca il messaggio con un for finito.
Se tutte le porte sono vuote si sospende su porteVuote.
Quando viene risvegliato entra in un ciclo while che gli fa testare le porte finché non trova effettivamente il messaggio, che in linea teorica prima o poi deve esserci perché qualche mittente ha fatto signal al server.
E' sbagliata come soluzione? Idee migliori?
Se è sempre vera la condizione:
quando il server si sveglia esiste necessariamente almeno un messaggio in coda
allora funziona per forza.
Non è neanche detto che sia un brutta soluzione perchè per pause di durata certa e molto breve uno spinning è vantaggioso rispetto ad una struttura di controllo concorrente relativamente più complicata come può essere un swap.
Lo sleep io lo metterei prima, magari un po' più breve (un bel po' più breve, diciamo 1ms anche se la granularità dello sleep una volta era sui 10-15ms).
Alternative idealmente ce ne sarebbero. Il protocollo che hai non è strano.
Io entro, lascio un messaggio sulla coda in entrata della mail box 1 e resto in attesa sulla coda di uscita della stessa mail box.
Il server viene svegliato dal gestore delle MailBox, prende il messaggio della mail box 1, lo processa e inserisce la risposta nella coda di uscita della MailBox 1.
Io che ero in attesa sulla coda di uscita della MailBox1 me ne vado con la mia risposta.
Alla fine tutto dipende da dove puoi mettere le mani.
Se è sempre vera la condizione:
quando il server si sveglia esiste necessariamente almeno un messaggio in coda
allora funziona per forza.
E' qui il punto. Non esiste necessariamente almeno un messaggio in coda ma esisterà. Il mittente sveglia il server prima di depositare effettivamente il messaggio questo perché appena deposita il messaggio si sospende su un semaforo.
Quindi è impossibile per me mittente segnalare il server dopo aver lasciato il messaggio.
In pratica lo segnalo un attimo prima... ma questa cosa ha il difetto che il mittente potrebbe segnalare e perdere il controllo della CPU prima di aver lasciato il messaggio... per questo il server svegliato va a cercare un messaggio che ancora non c'è.. ed è per questo che ho necessità del ciclo while dopo il risveglio...
Mi sono espresso male:
quando il server si sveglia esisterà necessariamente a breve un messaggio in coda
Ikon O'Cluster
23-08-2009, 15:07
Segnala entrambe le cose. Prima segnali che verrà messo un messaggio, quindi il server si sveglia, ma andrà in wait sulla coda di quella porta. Appena il client (dopo aver segnalato la sua intenzione di inserimento) effettua realmente l'inserimento allora il server si sveglia e riceve il messaggio. Ma non so se ho capito bene il problema...
Basterebbe il secondo segnale ma se il thread che deve segnalare si blocca subito dopo il deposito del messaggio chi lo manda?
Ikon O'Cluster
23-08-2009, 17:57
Scusate ora ho le idee un po' confuse perchè è passato troppo tempo dal corso di SISOP... Il codice non è formale.
MsgQueue coda[N] = {EMPTY, ..., EMPTY};
Semaforo sem = 0;
void serverBody() {
while(1) { // Corpo del server eseguito ciclicamente...
System.out.println("Controllo la presenza di messaggi.\n");
sem.wait();
System.out.println("Mi stanno inviando dei messaggi!\n");
System.out.println("Vediamo chi è...\n");
int c = 0;
while(1) {
if(!(coda[c].vuota())) {
System.out.println("Messaggio da Client" + c +"!!!");
Msg msg = coda[c].pop();
Msg newMsg = elabora(msg.info());
sendTo(c, newMsg);
System.out.println("Servito!");
break;
}
c=(c+1)%N;
}
}
}
Msg inviaMessaggio(int mittente, Msg msg) {
System.out.println("Segnalo al server.\n");
sem.signal();
System.out.println("Accodo messaggio.\n");
coda[mittente].push(msg);
Msg msg = receiveFrom(SERVER);
return msg;
}
In sostanza i client possono segnalare pure 1000 msg, il server ne elabora uno alla volta e quando il semaforo torna a 0 allora il server si blocca. Ogni volta che il server viene svegliato comincia a ciclare sulle code finchè il client non ha effettivamente accodato il messaggio. Appena lo trova elabora e risponde.
ATTENZIONE: Questa soluzione non gestisce i messaggi in ordine. Il client i che al tempo t1 invia un messaggio potrebbe essere gestito dal server dopo il client j che al tempo t2>t1 ha inviato il proprio messaggio.
Una soluzione un pò più "FAIR" è la seguente:
MsgQueue coda[N] = {EMPTY, ..., EMPTY};
Semaforo sem = 0;
int c = 0;
void serverBody() {
while(1) { // Corpo del server eseguito ciclicamente...
System.out.println("Controllo la presenza di messaggi.\n");
sem.wait();
System.out.println("Mi stanno inviando dei messaggi!\n");
System.out.println("Vediamo chi è...\n");
while(1) {
if(!(coda[c].vuota())) {
System.out.println("Messaggio da Client" + c +"!!!");
Msg msg = coda[c].pop();
Msg newMsg = elabora(msg.info());
sendTo(c, newMsg);
System.out.println("Servito!");
break;
}
c=(c+1)%N;
}
}
}
Msg inviaMessaggio(int mittente, Msg msg) {
System.out.println("Segnalo al server.\n");
sem.signal();
System.out.println("Accodo messaggio.\n");
coda[mittente].push(msg);
Msg msg = receiveFrom(SERVER);
return msg;
}
In questa soluzione tutti i client hanno la medesima priorità, mentre nella precedente venivano avvantaggiati quelli ad indice più basso. Adesso non esiste più starvation....
Quello è uno spinning ed è la soluzione che già sta usando.
Ikon O'Cluster
23-08-2009, 18:10
si ma mi convince più fatta come l'ho impostata io... :D
Stavo pensando... non sarebbe meglio se inserissi un time out nello spinning? :stordita:
Se il mittente dopo aver fatto signal sul semaforo per qualsiasi ragione non consegnasse il messaggio? avrei un Server in loop?
Se non sei certo al mille per mille che il messaggio ci sarà lo spin non va più bene.
Se non sei certo al mille per mille che il messaggio ci sarà lo spin non va più bene.
E come faccio ad essere certo? Io so che la signal è separata dall'inserimento del messaggio da poche istruzioni. Un mittente dopo la signal non può fare altro che rilasciare il messaggio.
Ma ipotizziamo il caso assurdo che il thread mittente venga killato dal SO dopo la signal (so che non è possibile ma facciamo finta che lo sia).. la soluzione è un time out?
Ci ho pensato per giorni e giorni e lo spinning sembra l'unica soluzione.
Non posso mettere le mani sul codice sottostante (Porta Sincrona).
Quando il mittente chiama Porta.send() si blocca dentro la Porta
Non posso segnalare il server dopo la send perché sono bloccato nella Porta
Unica soluzione lo spinning! ...o no? :stordita: :stordita: :stordita:
Come "come faccio ad essere certo"? :D
Devi guardare le istruzioni che stanno tra la notifica e il deposito, stabilire quali sono le condizioni che possono generare un mancato deposito e usarle per controllare lo spin.
Se la condizione è che il thread possa essere magicamente ucciso allora metterai come condizione del loop l'esistenza di almeno un thread client.
Il time-out pigliatutto è una soluzione un po' povera perchè una volta che scatta non sai perchè il messaggio sia andato perduto.
Se la condizione è che il thread possa essere magicamente ucciso allora metterai come condizione del loop l'esistenza di almeno un thread client.
L'unica possibilità è questa. Perché tra la notifica e il deposito non c'è nulla che possa portare ad un mancato inserimento del messaggio.
Più che "almeno l'esistenza di un thread client", che potrebbe essere true anche se il mittente muore, potrei fare un while con condizione:
"finché il thread mittente è vivo && non trovo messaggio"
Questo si potrebbe fare solo se esiste un modo in Java per sapere se il thread con id x è ancora vivo. Esiste? :stordita:
Se esiste potrei salvarmi l'id del thread mittente prima che questo notifichi il server e nello spinning verificare che il thread non sia misteriosamente scomparso.
Che ne pensi? :D
banryu79
07-09-2009, 17:20
@luxorl: che tipo di scenario stai cercando di gestire? il tipico caso dell'utonto che inciampa sul cavo dell'alimentazione staccando la spina al pc? :D
Se l'unica possibilità è che il Thread muoia inspiegabilmente allora tranquillizzati perchè i Thread non muoiono mai di morte violenta - a differenza ad esempio dei processi.
Se l'unica possibilità è che il Thread muoia inspiegabilmente allora tranquillizzati perchè i Thread non muoiono mai di morte violenta - a differenza ad esempio dei processi.
Ok :D
Grazie ;)
@luxorl: che tipo di scenario stai cercando di gestire? il tipico caso dell'utonto che inciampa sul cavo dell'alimentazione staccando la spina al pc? :D
:asd: più o meno :asd:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.