Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Cineca inaugura Pitagora, il supercomputer Lenovo per la ricerca sulla fusione nucleare
Cineca inaugura Pitagora, il supercomputer Lenovo per la ricerca sulla fusione nucleare
Realizzato da Lenovo e installato presso il Cineca di Casalecchio di Reno, Pitagora offre circa 44 PFlop/s di potenza di calcolo ed è dedicato alla simulazione della fisica del plasma e allo studio dei materiali avanzati per la fusione, integrandosi nell’ecosistema del Tecnopolo di Bologna come infrastruttura strategica finanziata da EUROfusion e gestita in collaborazione con ENEA
Mova Z60 Ultra Roller Complete: pulisce bene grazie anche all'IA
Mova Z60 Ultra Roller Complete: pulisce bene grazie anche all'IA
Rullo di lavaggio dei pavimenti abbinato a un potente motore da 28.000 Pa e a bracci esterni che si estendono: queste, e molte altre, le caratteristiche tecniche di Z60 Ultra Roller Complete, l'ultimo robot di Mova che pulisce secondo le nostre preferenze oppure lasciando far tutto alla ricca logica di intelligenza artificiale integrata
Renault Twingo E-Tech Electric: che prezzo!
Renault Twingo E-Tech Electric: che prezzo!
Renault annuncia la nuova vettura compatta del segmento A, che strizza l'occhio alla tradizione del modello abbinandovi una motorizzazione completamente elettrica e caratteristiche ideali per i tragitti urbani. Renault Twingo E-Tech Electric punta su abitabilità, per una lunghezza di meno di 3,8 metri, abbinata a un prezzo di lancio senza incentivi di 20.000€
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 12-01-2012, 08:02   #1
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
[Java] Dubbio su Multi-Thread e Multi-Core

Salve a tutti,
sto realizzando un software che esegue elaborazioni più o meno semplici su un elevato numero di oggetti, quindi per ottimizzarne l'esecuzione ho cercato di utilizzare i Thread. Ovviamente ho a disposizione macchine multicore per l'esecuzione del codice.

Ho notato però che l'occupazione dei core non è bilanciata, ma dopo una fase di startup in cui sembra esserlo (ad esempio: 4 core occupati all'incirca al 25%) si arriva a regime ad una occupazione del 100% di un solo core, gli altri sono allo 0%.

Cerco di spiegare a grandi linee l'algoritmo:
- Si alternano fasi in cui esiste solo il main thread a fasi in cui ne ho molti
- Su linux, tramite il comando top vedo in effetti questa alternanza di fasi, che oscillano da un solo core occupato a tutti i core occupati come detto sopra, ma solo nei primi minuti di elaborazione, a regime sembra prevalere un solo core al 100%
- I riferimenti alle istanze degli oggetti da elaborare sono memorizzate in un ArrayList, che viene opportunamente diviso dal numero di thread che voglio lanciare, dopodichè lancio i thread invocando il metodo start() e poi il metodo join() per far si che l'esecuzione del codice non vada avanti finché TUTTI non hanno completato la loro elaborazione.
- Ovviamente, se il carico di lavoro eseguito da ogni thread non è simile, si può verificare la situazione in cui i thread più veloci devono restare in attesa dei thread più lenti.
- Immagino che una situazione del genere (molto probabile, tra l'altro) possa portare all'occupazione al 100% di un solo core contro lo 0% degli altri (caso estremo in cui è rimasto un solo thread e tutti gli altri lo aspettano).
- Le operazioni eseguite da ogni thread possono coinvolgere anche metodi e oggetti condivisi, quindi ho dovuto usare il synchronize. Altra ragione che porta i Thread a restare in attesa del rilascio della risorsa condivisa...


Ecco, la situazione a grandi linee è questa. Quello che mi chiedo è:
1) Se ho 4 core ed istanzio ad esempio 100 Thread, questi come vengono eseguiti? La CPU se ne prende in carico, ad esempio, 4 e gli altri 96 sono in attesa di essere eseguiti? Quindi se ho uno dei primi 4 thread in attesa del rilascio di una risorsa condivisa, uno degli altri 96 può essere avviato? Oppure i 100 Thread vengono tutti avviati nello stesso istante e procedono in parallelo e quindi si troveranno TUTTI (meno uno) in attesa della risorsa condivisa?
2) Ho visto che esistono comandi come yeld() e wait() per far si che un Thread rilasci le risorse ad un altro, ma poi va risvegliato con notify(). Dovrei forse usare yeld() prima di tentare di accedere alla risorsa condivisa?
3) Penso che il modo per ridurre il tempo di esecuzione totale sia proprio quello di far si che ogni core sia sempre ben occupato, facendo in modo che non appena un Thread si trova a dover attendere il rilascio della risorsa condivisa, se ne attivi subito un altro, per non sprecare cicli di clock...


Qualcuno più esperto in programmazione parallela ha dei consigli?


P.S. Tra l'altro, non mi convince molto il fatto che l'occupazione arrivi solo al 25%. Ho fatto alcune classi di test per prendere mano con il multi thread e in questi casi semplici ho verificato che tutti i core sono occupati al 100% fino alla fine dell'esecuzione. Mentre nell'algoritmo reale cio' non accade, eppure la strategia bene o male è quella, cambia solo l'elaborazione all'interno del run(). Nei casi di test gli faccio eseguire un paio di operazioni algebriche, nel caso reale invoco altri metodi...

Ultima modifica di Lim : 12-01-2012 alle 08:10. Motivo: Aggiunto un'ulteriore chiarimento
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 10:43   #2
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
Ciao, cercherò di essere il più conciso possibile.
Premessa: un thread può essere in diverse "code":

- coda dei thread in esecuzione (una coda per ogni core costituita da max 1 elemento)
- coda dei thread pronti (in attesa di esser mandati in esecuzione su una coda di esecuzione)
- N code dei thread bloccati per ogni risorsa condivisa (in attesa di accedere ad una risorsa condivisa)


1) Se hai 4 core, 4 thread possono potenzialmente essere in esecuzione contemporaneamente, MA, potrebbe succedere che, poiché tutt i thread devono accedere ad un risorsa condivisa (es con il metodo synchronized) essi vadano tutti a bloccarsi quindi saresti nella situazione di 1 thread in esecuzione e 99thread bloccati. Però potresti anche trovarti nel caso in cui hai 1 thread in esecuzione nella sezione critica e altri 3 thread che ci "stanno per arrivare" es.

int b=10; <--- th2, th3, th4 sono quì
synchronized(){
... <----- th1 è quì
a=1;
...
}

In questo caso hai effettivamente 4 thread in esecuzione. Tuttavia possono avvenire infiniti di casi differenti.

Esempio: 4 thread in esecuzione, 50 pronti e 46 bloccati su possibili diverse code di bloccaggio.

Attenzione: quando usi il synchronized(...) esso è bloccante solo se c'è un thread in esecuzione dentro l'oggetto a cui si riferisce la synchronized. Diverse synchronized su oggetti diversi formano code di bloccaggio differenti.

Non rispondo alle altre domande per non confonderti di più nel caso non sia stato abbastanza chiaro.

PPS. se vuoi ottimizzare il codice al massimo è sufficiente che non usi sezioni critiche. Ovviamente mantenendo la consistenza dei dati. Quindi, meno sezioni critiche -> meno thread bloccati -> più thread nella coda dei processi pronti -> più thread in esecuzione.
Il "meno" non è inteso come quantità di sezioni critiche, ne basta una che ti blocca tutto.
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX

Ultima modifica di starfred : 12-01-2012 alle 10:47.
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 11:00   #3
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da starfred Guarda i messaggi
Ciao, cercherò di essere il più conciso possibile.
Premessa: un thread può essere in diverse "code":

- coda dei thread in esecuzione (una coda per ogni core costituita da max 1 elemento)
- coda dei thread pronti (in attesa di esser mandati in esecuzione su una coda di esecuzione)
- N code dei thread bloccati per ogni risorsa condivisa (in attesa di accedere ad una risorsa condivisa)
Credo che fin qui mi è chiaro. Quindi se creo 100 thread, 4 vanno ai core, 96 nella lista dei thread pronti, quindi in realtà sono fermi ed attendono di essere eseguiti dal primo core che si libera, giusto?

Quote:
Originariamente inviato da starfred Guarda i messaggi
1) Se hai 4 core, 4 thread possono potenzialmente essere in esecuzione contemporaneamente, MA, potrebbe succedere che, poiché tutt i thread devono accedere ad un risorsa condivisa (es con il metodo synchronized) essi vadano tutti a bloccarsi quindi saresti nella situazione di 1 thread in esecuzione e 99thread bloccati. Però potresti anche trovarti nel caso in cui hai 1 thread in esecuzione nella sezione critica e altri 3 thread che ci "stanno per arrivare" es.

int b=10; <--- th2, th3, th4 sono quì
synchronized(){
... <----- th1 è quì
a=1;
...
}

In questo caso hai effettivamente 4 thread in esecuzione. Tuttavia possono avvenire infiniti di casi differenti.

Esempio: 4 thread in esecuzione, 50 pronti e 46 bloccati su possibili diverse code di bloccaggio.
Ok, tutto chiaro.


Quote:
Originariamente inviato da starfred Guarda i messaggi
Attenzione: quando usi il synchronized(...) esso è bloccante solo se c'è un thread in esecuzione dentro l'oggetto a cui si riferisce la synchronized. Diverse synchronized su oggetti diversi formano code di bloccaggio differenti.
Un momento, tu parli di oggetto. Vuoi dire che gli altri thread non potrebbero accedere ad altri metodi NON sincronizzati dell'oggetto in questione?
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 11:03   #4
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
no voglio dire, se hai 2 thread che fanno:

il primo thread:
synchronized (a){}


il secondo thread:
synchronized(b){}

Essi sono eseguiti in parallelo senza nessun bloccaggio
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 11:07   #5
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
*edit
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 12-01-2012 alle 11:09.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 11:15   #6
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da starfred Guarda i messaggi
no voglio dire, se hai 2 thread che fanno:

il primo thread:
synchronized (a){}


il secondo thread:
synchronized(b){}

Essi sono eseguiti in parallelo senza nessun bloccaggio
Cerco di spiegarti il mio nuovo dubbio.

Ho un oggetto che ha diversi metodi NON sincronizzati ed uno solo sincronizzato.

Ogni Thread deve accedere a questo stesso oggetto ed invocare alcuni suoi metodi, ad un certo punto dell'esecuzione, invocano il metodo sincronizzato. Il primo che ci arriva lo blocca. Quello che mi chiedo io è se gli altri thread + lenti possono continuare ad invocare i metodi non sincronizzati (finchè non arrivano a quello bloccato) o si bloccano subito perché stanno utilizzando le funzionalità (metodi) dello stesso oggetto del thread bloccante?

Spero di essermi spiegato
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 11:37   #7
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
Sperando di aver capito, ti riferisci ad una situazione iniziale dove hai:

100 thread totali, 4 in esecuzione, 0 bloccati.



Codice:
// 4 thread in esecuzione parallela
int b=10; <--- th1, th2, th3, th4 sono quì
c=2
d=3
synchronized(){
... 
a=1;
...
}
Quando arrivi a:

Codice:
//Hai sempre 4 thread in esecuzione parallela
int b=10; <--- th2, th3, th4 sono quì
c=2
d=3
synchronized(){
... <--- th1 è quì
a=1;
...
}
Supponiamo che th2 arrivi alla sezione critica, esso si bloccherà e finirà nella coda dei processi bloccati, a questo punto lo scheduler selezionerà un thread th5 dalla coda dei processi pronti e lo manderà in esecuzione, quindi avremo


Codice:
int b=10; th5 <--- è quì
c=2
d=3 <--- th3, th4 sono quì
synchronized(){
... <--- th1 è quì
a=1;
...
}
Quindi abbiamo sempre 4 thread in esecuzione, 95 thread pronti e 1 bloccato.
Ovviamente può accadere che se la sezione critica è molto lunga, prima o poi tutti i thread pronti finiscono nella coda bloccati, a quel punto avremmo un solo thread in esecuzione. Quando esso finirà la sezione critica, risveglierà un thread dalla coda bloccati e, ad esclusione di particolari metodi di programmazione, lo rimette in quella pronti successivamente lo scheduler selezionerà un thread dalla coda del processi pronti e lo manderà in esecuzione.
Quindi avremo

2 thread in esecuzione
98 thread bloccati

Ps. se hai utilizzato metodi wait() e notify() la situazione è un po' differente...
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 12:04   #8
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da starfred Guarda i messaggi
Sperando di aver capito, ti riferisci ad una situazione iniziale dove hai:

100 thread totali, 4 in esecuzione, 0 bloccati.



Codice:
// 4 thread in esecuzione parallela
int b=10; <--- th1, th2, th3, th4 sono quì
c=2
d=3
synchronized(){
... 
a=1;
...
}
Quando arrivi a:

Codice:
//Hai sempre 4 thread in esecuzione parallela
int b=10; <--- th2, th3, th4 sono quì
c=2
d=3
synchronized(){
... <--- th1 è quì
a=1;
...
}
Supponiamo che th2 arrivi alla sezione critica, esso si bloccherà e finirà nella coda dei processi bloccati, a questo punto lo scheduler selezionerà un thread th5 dalla coda dei processi pronti e lo manderà in esecuzione, quindi avremo


Codice:
int b=10; th5 <--- è quì
c=2
d=3 <--- th3, th4 sono quì
synchronized(){
... <--- th1 è quì
a=1;
...
}
Quindi abbiamo sempre 4 thread in esecuzione, 95 thread pronti e 1 bloccato.
Ovviamente può accadere che se la sezione critica è molto lunga, prima o poi tutti i thread pronti finiscono nella coda bloccati, a quel punto avremmo un solo thread in esecuzione. Quando esso finirà la sezione critica, risveglierà un thread dalla coda bloccati e, ad esclusione di particolari metodi di programmazione, lo rimette in quella pronti successivamente lo scheduler selezionerà un thread dalla coda del processi pronti e lo manderà in esecuzione.
Quindi avremo

2 thread in esecuzione
98 thread bloccati

Ps. se hai utilizzato metodi wait() e notify() la situazione è un po' differente...

Penso che la situazione in cui mi trovo è proprio questa. In effetti nei primi istanti ho un'occupazione equa dei core, mentre a regime ne ho solo uno occupato al 100%. Devono aver raggiunto tutti il punto critico e sono tutti nella coda dei bloccati, quindi da un'esecuzione potenzialmente parallela, sono tornato ad una sequenziale, senza vantaggi evidenti. Devo rivedere il codice in modo sostanziale temo...

Mi consigli qualche approccio specifico? o devo semplicemente ridurre il più possibile la presenza di risorse condivise e metodi synchronized? e la durata/complessita delle eventuali porzioni bloccanti?


PS Al momento non ho utilizzato né wait() né notify().
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 12:33   #9
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
Se effettivamente avviene il bloccaggio dei thread, per prima cosa ti consiglio di non utilizzare 100 thread , se sei su un 4 core, credo che 6-8 thread vadano bene, ma dipende molto dall'applicazione.

Per quel che riguarda la sezione critica il tutto dipende dal tipo di risorsa condivisa e distinguere tra operazioni distruttive "scritture" e operazioni non distruttive "letture".
Per esempio il classico problema lettori-scrittori ti può far vedere come posso avere più lettori paralleli su una risorsa condivisa e con un solo scrittore attivo alla volta.

Un altro consiglio è, ma questo vale per la programmazione in generale, scrivere è meno possibile su disco ed in generale su IO. Se dentro la sezione critica hai una write su un file, stai sicuro al 99% che tutti i thread si bloccano in attesa dell'operazione.
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 12:41   #10
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da starfred Guarda i messaggi
Se effettivamente avviene il bloccaggio dei thread, per prima cosa ti consiglio di non utilizzare 100 thread , se sei su un 4 core, credo che 6-8 thread vadano bene, ma dipende molto dall'applicazione.
Si, era solo un esempio Sto facendo delle prove tra 1 e 4 volte il numero di core disponibili.
Mi sembra di intuire che tu suggerisci al max 2 per ogni core, verificherò meglio dopo la revisione del codice.


Quote:
Per quel che riguarda la sezione critica il tutto dipende dal tipo di risorsa condivisa e distinguere tra operazioni distruttive "scritture" e operazioni non distruttive "letture".
Per esempio il classico problema lettori-scrittori ti può far vedere come posso avere più lettori paralleli su una risorsa condivisa e con un solo scrittore attivo alla volta.
Ho fatto una semplice classe di test in cui condivido una lista di elementi tra due oggetti.
Uno dei due la gestisce tramite Vector. Ho verificato che anche la sola lettura di un elemento del Vector rallenta in modo pazzesco l'esecuzione. Conosci altre strutture dati che non blocchino anche in lettura?


Quote:
Un altro consiglio è, ma questo vale per la programmazione in generale, scrivere è meno possibile su disco ed in generale su IO. Se dentro la sezione critica hai una write su un file, stai sicuro al 99% che tutti i thread si bloccano in attesa dell'operazione.
In effetti scrivo su disco, ma solo al termine di tutti i thread, quindi non penso che impatti affatto...
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 13:02   #11
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
Quello che dico io è: prova a levare tutte le sezioni critiche, leggi una variabile condivisa e fai delle operazioni random su di essa.

es. 4 thread in parallelo che eseguono

Codice:
while(1){
int b=int_condiviso;
b=b+rand();
}
Questo dovrebbe darti un occupazione vicino al 100% dei core.
Se invece fai

Codice:
synchronized(){
while(1){
int b=int_condiviso;
b=b+rand();
}
}
Vedrai come le prestazioni si abbasseranno drasticamente.

I codici sono corretti entrambi ma presentano sostanziali prestazioni.
Il fatto che sia corretto mettere int_condiviso fuori dal synchronized() è dovuto al fatto che noi non stiamo modificando tale variabile ma la stiamo solo leggendo.
Spero di averti dato uno spunto di riflessione sul miglioramento da fare sul codice.
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 13:28   #12
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
@Lim: forse è inutile dirlo ma questo uso del synchronized (lock implicito):
Codice:
class ClassName {
    private Type field1;
    private AnotherType field2;

    public synchronized void method1() {
        // doSomething...
        // doSomething with field1
        // doSomethingElse...
        // doSomething with field2
    }
}
è un modo più sintetico per dire questo (qui il lock è esplicito):
Codice:
class ClassName {
    private Type field1;
    private AnotherType field2;

    public void method1() {
        synchronized(this) {
            // doSomething...
            // doSomething with field1
            // doSomethingElse...
            // doSomething with field2
        }
    }
}
Questo giusto per ricordare che in Java ciò che si "sincronizza" con dei lock non sono i metodi, ma i campi e per spiegare questo:
Quote:
Uno dei due la gestisce tramite Vector. Ho verificato che anche la sola lettura di un elemento del Vector rallenta in modo pazzesco l'esecuzione.
Se vai a vedere i javadoc di Vector vedrai che tutti i metodi pubblici sono dichiarati con synchronized, ergo quando condividi una stessa istanza di Vector tra vari thread, ogni thread che chiama un suo metodo tenta di acquisire l'unico lock sull'istanza stessa (il this).

Per riprende con l'esempio qui sopra, il lock sul "this" sta regolando l'accesso a entrambi i campi (field1 e field2).
Ma supponiamo che non occorra regolare l'accesso in maniera esclusiva su filed1 (AType) ma solo su filed2 (AnotherType).
Si potrebbe allora fare così:
Codice:
class ClassName {
    private Type field1;
    private AnotherType field2;

    public void method1() {
        // doSomething...
        // doSomething with field1
        // doSomethingElse...
        synchronized(this) {
            // doSomething with field2
        }
    }
}
A questo punto si può regolare l'accesso esclusivo in modo più fine, usando degli appositi oggetti come lock invece che il lock implicito sull'istanza this:
Codice:
class ClassName {
    private Type field1;

    private AnotherType field2;
    private Object filed2Lock;

    public void method1() {
        // doSomething...
        // doSomething with field1
        // doSomethingElse...
        synchronized(filed2Lock) {
            // doSomething with field2
        }
    }
}
Sincertamente spero di non averti detto nulla di nuovo o che ti faccia sorgere dei dubbi...
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 12-01-2012 alle 13:42.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 14:15   #13
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da banryu79 Guarda i messaggi
...Per riprende con l'esempio qui sopra, il lock sul "this" sta regolando l'accesso a entrambi i campi (field1 e field2).
Ma supponiamo che non occorra regolare l'accesso in maniera esclusiva su filed1 (AType) ma solo su filed2 (AnotherType).
Si potrebbe allora fare così:
Codice:
class ClassName {
    private Type field1;
    private AnotherType field2;

    public void method1() {
        // doSomething...
        // doSomething with field1
        // doSomethingElse...
        synchronized(this) {
            // doSomething with field2
        }
    }
}
A questo punto si può regolare l'accesso esclusivo in modo più fine, usando degli appositi oggetti come lock invece che il lock implicito sull'istanza this:
Codice:
class ClassName {
    private Type field1;

    private AnotherType field2;
    private Object filed2Lock;

    public void method1() {
        // doSomething...
        // doSomething with field1
        // doSomethingElse...
        synchronized(filed2Lock) {
            // doSomething with field2
        }
    }
}
Sincertamente spero di non averti detto nulla di nuovo o che ti faccia sorgere dei dubbi...

Grazie per l'ottimo chiarimento.
Diciamo che per semplicità ho utilizzato dei lock impliciti per risolvere alcuni problemi di concorrenza.
Quello che voglio fare ora infatti è un'ottimizzazione più fine del codice.

Dall'esempio di codice che hai postato però un piccolo dubbio me l'hai fatto sorgere. Giustamente, non applico il lock al metodo, ma ai campi che lui manipola. Quindi il lock sul this blocca TUTTI i campi dell'istanza, anche se sono invocati/manipolati da una qualunque altra porzione di codice (altri metodi della stessa istanza, da altre classi ecc...).
Invece applicando il lock ad un campo specifico (field2Lock) sincronizzo solo in base ad esso, quindi gli altri campi restano utilizzabili, giusto?
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 14:34   #14
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da Lim Guarda i messaggi
Giustamente, non applico il lock al metodo, ma ai campi che lui manipola.
Dire così però non è corretto, e può trarre in inganno. Non è che un lock si "applica" ai campi. Un lock è associato a una sezione (di codice) critica (dove si eseguono operazioni sui campi).
Dice: caro thread in esecuzione, prima di poter entrare e procedere nella sezione critica, io devo essere disponibile ed entrare in tuo possesso esclusivo. Quando esci dalla sezione critica, io non sono più in tuo possesso e torno disponibile.

Quote:
Originariamente inviato da Lim Guarda i messaggi
Quindi il lock sul this blocca TUTTI i campi dell'istanza, anche se sono invocati/manipolati da una qualunque altra porzione di codice (altri metodi della stessa istanza, da altre classi ecc...).
Per quanto detto prima, questo è valido se TUTTI gli accessi ai campi dell'istanza sono protetti dallo STESSO lock (il this o qualsiasi altro oggetto usato come lock).

Esempio:
Codice:
class ClassName {
    private Object field1 = bla...
    private Object field2 = bla...

    public synchronized void method1() {
        // operate on field1...
        // operate on field2...
    }

    public synchronized void method2() {
        // operate on field1...
        // operate on field2...
    }

    public void method3() {
        // operate on field2...
    }
  
}
Qui tutti gli accessi a field1 sono "sincronizzati" (TUTTI gli accessi a filed1 sono dentro sezioni critiche collegate allo STESSO lock, il this), mentre gli accessi field2 no... in method3 non viene richiesta l'acquisizione di alcun lock, ergo le operazioni su filed2 da parte di diversi thread che chiamano il metodo non sono "sincronizzate".

A titolo di ulteriore esempio, visto che qui sopra ci interessa rendere esclusivo tra i vari thread solo l'uso (gli accessi) di field1 mentre field2 no (per qualsivoglia ragione che ora non ha importanza) si può dire così:
Codice:
class ClassName {
    private Object field1 = bla...
    private Object field2 = bla...

    public void method1() {
        synchronized(this) {
            // operate on field1
        }
        // operate on field2
    }

    public void method2() {
        synchronized(this) {
            // operate on field1
        }
        // operate on field2
    }

    public void method3() {
        // operate on field2
    }
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 12-01-2012 alle 14:45.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 14:40   #15
starfred
Senior Member
 
Iscritto dal: Jul 2011
Messaggi: 381
Ci tengo a precisare in ogni caso il synchronized si riferisce sempre e comunque ad oggetti e non a porzioni di codice. Per tutto il resto sono d'accordo.
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX
starfred è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 14:52   #16
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Quote:
Originariamente inviato da banryu79 Guarda i messaggi

Esempio:
Codice:
class ClassName {
    private Object field1 = bla...
    private Object field2 = bla...

    public synchronized void method1() {
        // operate on field1...
        // operate on field2...
    }

    public synchronized void method2() {
        // operate on field1...
        // operate on field2...
    }

    public void method3() {
        // operate on field2...
    }
  
}
Qui tutti gli accessi a field1 sono "sincronizzati" (tutti gli accessi sono dentro sezioni critiche collegate allo stesso lock, il this), mentre gli accessi field2 no... in method3 non viene richiesta l'acquisizione di alcun lock, ergo le operazioni su filed2 da parte di diversi thread che chiamano il metodo non sono "sincronizzate".

Perfetto, grazie mille. Credo che hai sciolto anche l'ultimo dubbio (spero...), quindi per sicurezza faccio un controesempio riprendendo il tuo codice:

Se ho 4 thread attivi, di cui per varie ragioni 3 sono arrivati al punto di invocare method1() ed uno tenta di invocare method3(), dovrebbe accadere che uno dei 3 thread acquisisce il lock ed esegue method1(), mentre gli altri due si bloccano e vanno in attesa. Il 4° thread, invece continua l'esecuzione in parallelo al 1° thread, quindi:

thread1 --> esegue method1()
thread2 --> bloccato
thread3 --> bloccato
thread4 --> esegue method3()


Fin qui è giusto?

E se sia method1() che method3() tentassero di scrivere field2? Non si genererebbe alcuna eccezione, ma soltanto una probabile alterazione del risultato desiderato?

Esempio:

method1(), tra le varie operazioni, esegue anche: field2++;
method3(), tra le varie operazioni, esegue anche: field2*2;

Al termine di entrambi i thread posso avere un risultato differente per field2...

E' corretto il ragionamento?
Lim è offline   Rispondi citando il messaggio o parte di esso
Old 12-01-2012, 17:29   #17
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da Lim Guarda i messaggi
Se ho 4 thread attivi, di cui per varie ragioni 3 sono arrivati al punto di invocare method1() ed uno tenta di invocare method3(), dovrebbe accadere che uno dei 3 thread acquisisce il lock ed esegue method1(), mentre gli altri due si bloccano e vanno in attesa. Il 4° thread, invece continua l'esecuzione in parallelo al 1° thread, quindi:

thread1 --> esegue method1()
thread2 --> bloccato
thread3 --> bloccato
thread4 --> esegue method3()


Fin qui è giusto?
Sì.

Quote:
Originariamente inviato da Lim Guarda i messaggi
E se sia method1() che method3() tentassero di scrivere field2? Non si genererebbe alcuna eccezione, ma soltanto una probabile alterazione del risultato desiderato?
Sì. Le eccezioni non c'entrano nulla. Nel caso da te ipotizzato si ha una race condition su field2, e il risultato finale prodotto dopo un "passaggio" di tutti i thread coinvolti dipende da questioni di timing/scheduling di detti thread. Tra l'altro, il risultato in questione, a parità di valore iniziale, dopo il "passaggio" di tutti i thread coinvolti può variare da esecuzione ad esecuzione.

Quote:
Originariamente inviato da Lim Guarda i messaggi
Esempio:

method1(), tra le varie operazioni, esegue anche: field2++;
method3(), tra le varie operazioni, esegue anche: field2*2;

Al termine di entrambi i thread posso avere un risultato differente per field2...

E' corretto il ragionamento?
In linea di massima sì. Vedi quanto detto qua sopra.

Se mastichi inglese prova a leggerti questo: "Java Tutorial, Synchronization" dovrebbe fugare eventuali dubbi resiudi.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 12-01-2012 alle 18:02.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 13-01-2012, 09:52   #18
Lim
Senior Member
 
L'Avatar di Lim
 
Iscritto dal: Dec 2000
Messaggi: 501
Grazie mille, ora è tutto chiaro!
Lim è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Cineca inaugura Pitagora, il supercomputer Lenovo per la ricerca sulla fusione nucleare Cineca inaugura Pitagora, il supercomputer Lenov...
Mova Z60 Ultra Roller Complete: pulisce bene grazie anche all'IA Mova Z60 Ultra Roller Complete: pulisce bene gra...
Renault Twingo E-Tech Electric: che prezzo! Renault Twingo E-Tech Electric: che prezzo!
Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media Il cuore digitale di F1 a Biggin Hill: l'infrast...
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica DJI Osmo Mobile 8: lo stabilizzatore per smartph...
Samsung Galaxy S26 Ultra: una sola novit...
Google prepara Gemini 3 Pro e Nano Banan...
TVS non è solo moto e scooter: ec...
Alexa+ arriva su BMW: gli automobilisti ...
Gemini Deep Research arriva su Google Fi...
Rinvii a catena, Marvel 1943: Rise of Hy...
Xiaomi inaugura uno spazio dedicato ai f...
Rilasciate le specifiche di Bluetooth 6....
L'obiettivo che mette tutto a fuoco: la ...
Meta avrebbe raccolto fino al 10% dei ri...
NVIDIA DGX Spark e videogiochi? Una pess...
Serie Oppo Reno15 confermata: arriva il ...
UPDF 2025: l'editor PDF che fa (quasi) t...
Partono altri sconti pesanti su Amazon, ...
OpenAI senza freni: centinaia di miliard...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 17:55.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Served by www3v