|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
[Java] va bene questa implementazione?
tempo fa avevo scritto sul forum in merito alla stessa questione: realizzare in Java qualcosa che corrisponda praticamente a ciò che in Win32 è un evento auto-reset (risveglia i thread che lo attendono e si resetta automaticamente a non-signaled). PGI-Bis mi aveva dato una sua versione, che poi io ho perfezionato aggiungendo un paio di cose.
ora visto che io di wait e notifyAll non ci ho capito una beneamata, volevo appunto sapere se c'era qualche problema in quel paio di cose o se va ancora bene ecco qua la mia implementazione: Codice:
public class Event
{
private volatile boolean wakeUp;
public Event()
{
wakeUp = false;
}
public synchronized void waitCondition() throws InterruptedException
{
wakeUp = false;
while (!wakeUp)
{
wait();
}
}
public synchronized void waitUninterruptibly()
{
while (true)
{
try
{
wait();
break;
}
catch (InterruptedException e)
{
}
}
}
public synchronized void notifyCondition()
{
wakeUp = true;
notifyAll();
}
}
Ultima modifica di 71104 : 28-12-2006 alle 19:29. |
|
|
|
|
|
#2 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
se ricordo bene la versione originaria di PGI-Bis faceva più o meno così:
Codice:
public class Event
{
private volatile boolean wakeUp;
public Event()
{
wakeUp = false;
}
public synchronized void waitCondition() throws InterruptedException
{
while (!wakeUp)
{
wait();
}
}
public synchronized void notifyCondition()
{
wakeUp = true;
notifyAll();
}
}
c'è modo (semplice ^^) di risolvere questo problema? |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Che succede se (A, B, C eccetera sono Thread):
A piglia il lock, invoca waitCondition, wakeup diventa false, A si addormenta e il lock viene liberato. B piglia il lock. C, che vuole invocare waitCondition, piglia il lock. B, che ha il lock, invoca notifyCondition, wakeup diventa true, A si risveglia, b rilascia il lock al termine dell'invocazione. A deve acquisire il lock prima di eseguire il metodo waitCondition dal punto successivo al wait() ma lo schedulatore gli preferisce C, anche lui in attesa del lock. C piglia il lock, invoca waitCondition, wakeup diventa false, C si addormenta e rilascia il lock. A finalmente trova la porta aperta, piglia il lock e TA DAH!, wakeup è false, quindi torna a dormire. Azzerare l'evento fa sorgere problemi "esistenziali": e se quando lo azzero c'è ancora qualcuno che non si è stava dormendo da prima ma non ha ancora avuto modo di svegliarsi? Non essendo garantito l'ordine di risveglio, quello che ti trovi a dover accettare è che nessun principe venga mai a baciare una delle tue belle addormentate. Quello che ritengo succeda in WIN32 è che l'azzerameno dell'evento causi la produzione di un nuovo lock contro il quale sono fatti "scontrare" tutti coloro che invochino waitCondition successivamente al notifyCondition. Dopodichè passa a notificare a tutti quelli che erano in attesa sul vecchio lock che è ora di svegliarsi. Una cosa del genere: Codice:
public class Event {
private boolean wakeUp; //false per inizializzazione
public synchronized void waitCondition() throws InterruptedException {
while (!wakeUp) {
wait();
}
}
public synchronized void notifyCondition() {
wakeUp = true;
notifyAll();
}
}
Codice:
public class ResettableEvent extends Event {
private volatile Event event = new Event();
public void waitCondition() throws InterruptedException {
event.waitCondition();
}
public void notifyCondition() {
Event cache = event;
event = new Event();
cache.notifyCondition();
}
}
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#4 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
http://www.reactos.org/generated/dox...ent_8c.html#a6 però leggendo quel codice non noto blocchi se non quel KeAcquireDispatcherDatabaseLock. è difficile capirne il significato perché non so che cosa contenga questo "dispatcher database" (non credo sia roba dell'NT originale). a meno che il "dispatcher database" non sia un concetto noto di teoria dei sistemi operativi che io nella mia ignoranza non conosco ![]() comunque tornando alla classe Event, sostanzialmente le mie necessità ammontano all'usare un oggetto Event più volte tra due threads, uno che aspetta e un altro che notifica. ma in futuro il thread che aspetta potrebbe diventare al plurale -- N threads che aspettano. viceversa quello che notifica sempre uno rimane. poiché lo stesso oggetto deve essere usato più volte (il thread che aspetta deve aspettare in loop e vorrei che non si risvegliasse due volte a causa della stessa notifica), da qualche parte un reset del flag mi è necessario. come dove e quando lo resetto? ^^ a dire il vero vorrei evitare di usare una seconda classe che usa ereditarietà ed incapsulamento insieme :| |
|
|
|
|
|
|
#5 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
per curiosità: se invece mettessi il reset subito dopo il while del metodo di attesa che cosa otterrei? otterrei che per svegliare N thread in attesa dovrei chiamare il metodo di notifica N volte, ho intuito bene?
Codice:
public class Event
{
private volatile boolean wakeUp;
public Event()
{
wakeUp = false;
}
public synchronized void waitCondition() throws InterruptedException
{
while (!wakeUp)
{
wait();
}
wakeUp = false;
}
public synchronized void notifyCondition()
{
wakeUp = true;
notifyAll();
}
}
|
|
|
|
|
|
#6 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
|
|
|
|
|
|
|
#7 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
|
|
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Alla fin fine non so neanche perchè io abbia reso ResettableEvent estensione di Event. Quando uno vive di certezze
Se non ti interessa la relazione tra i due, non vedo problemi nel separarli (cioè ResettableEvent NON extends Event).
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#9 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
ho scritto un'altra implementazione che con un solo oggetto Event dovrebbe risolvere il problema che hai descritto al post #3. però fa uso di un ReentrantReadWriteLock
Codice:
public class Event
{
private volatile boolean wakeUp;
private ReentrantReadWriteLock notificationLock;
public Event()
{
wakeUp = false;
notificationLock = new ReentrantReadWriteLock();
}
public synchronized void waitCondition() throws InterruptedException
{
notificationLock.readLock().lock();
do
{
wait();
}
while (!wakeUp);
notificationLock.readLock().unlock();
}
public synchronized void waitUninterruptibly()
{
while (true)
{
try
{
wait();
break;
}
catch (InterruptedException e)
{
}
}
}
public synchronized void notifyCondition()
{
wakeUp = true;
notifyAll();
reset();
}
private void reset()
{
notificationLock.writeLock().lock();
wakeUp = false;
notificationLock.writeLock().unlock();
}
}
2) chiaramente stavo pensando a qualcosa di più semplice, e chiaramente qualcosa di più semplice esiste visto che tu l'hai realizzata con un Event al posto di un ReentrantReadWriteLock |
|
|
|
|
|
#10 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
LOOOOOOOL, ma scusa ora che ci penso
Codice:
public class Lol
{
private ReentrantReadWriteLock notificationLock;
public Lol()
{
notificationLock = new ReentrantReadWriteLock();
notificationLock.writeLock().lock();
}
public void waitCondition()
{
notificationLock.readLock().lock();
notificationLock.readLock().unlock();
}
public void notifyCondition()
{
notificationLock.writeLock().unlock();
notificationLock.writeLock().lock();
}
}
Ultima modifica di 71104 : 29-12-2006 alle 15:40. |
|
|
|
|
|
#11 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
adesso per curiosità voglio andare a leggermi il codice di ReentrantReadWriteLock
|
|
|
|
|
|
#12 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
![]() mi fido va' PGI-Bis, che ne pensi? quest'ultima lamerata col ReentrantReadWriteLock ha problemi? ^^ |
|
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Non risolve il problema della contesa. Potrebbe tuttavia se dichiarassi il lock "fair" (il costruttore che accetta un boolean). In quel caso la contesa per il lock favorisce il Thread che attende da più tempo. Non ti serve, tuttavia, un ReadWriteLock per fare questo, ti basta un ReentrantLock.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#14 | ||
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
Quote:
cos'ha di superfluo il ReentrantReadWriteLock? grazie per la pazienza |
||
|
|
|
|
|
#15 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Perchè siano sbloccati tutti quelli in attesa quello che ci serve è che prima del reset tutti i thread bloccati abbiano l'occasione di eseguire il codice che determina il proseguimento del loro lavoro.
Normalmente non c'è un ordine nella scelta tra chi sarà il prossimo ad acquisire il lock (tanto che si tratti di un java.util.concurrent.Lock quanto di un blocco synchronized). Abbiamo 50 thread che si sono bloccati e che per potersi svegliare devono prima acquisire il lock. Il 51° Thread arriva e libera tutti. Il reset opera dicendo al 51° Thread che deve riacquisire il lock. Benchè il 51° sia l'ultimo arrivato e ci siano altri 50 in coda prima di lui nulla vieta che sia proprio il 51° ad essere il primo ad acquisire il lock (meglio sarebbe parlare di mutex). Ma se il lock è "fair", cioè se garantisce che chi attende da più tempo acquisisca per primo il lock quando questo sia disponibile, allora tra il: notificationLock.unlock(); e notificationLock.lock(); del "risveglio", tutti i 50 Thread in coda avranno l'opportunità di acquisire e rilasciare il mutex conteso. ReadWriteLock usa due mutex per consentire delle ottimizzazioni (di cui non ho assolutamente idea) nella gestione della contesa tra due classi di Thread: una che vuole leggere qualcosa e una che cambia il valore di quello che gli altri leggono. Suppone inoltre che la classe dei lettori abbia un numero più elevato di componenti rispetto a quella degli scrittori (idealmente uno solo). Puoi certamente avvantaggiarti di questa caratteristica ma non è strettamente necessaria al fine di interrompere l'esecuzione di un manipolo di Thread finchè qualcun'altro non decida che è ora di svegliarsi. Il true nel costruttore di LOCK è quello che stabilisce il rispetto dell'ordine temporale di acquisizione del mutex. Dal punto di vista delle librerie. Poi c'è il problema delle politiche di scheduling del sistema operativo. Non è detto che l'essere arrivati prima significhi per ciò stesso che il Thread sarà effettivamente svegliato dallo schedulatore di sistema. Ritengo tuttavia che sia un caso limite. Codice:
import java.util.concurrent.locks.*;
public class Event {
private final ReentrantLock LOCK = new ReentrantLock(true);
public Event() {
LOCK.lock();
}
public void waitCondition() {
LOCK.lock();
LOCK.unlock();
}
public void notifyCondition() {
LOCK.unlock();
LOCK.lock();
}
}
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#16 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
piuttosto devo ancora capire bene qual è il problema usando il ReadWriteLock... ![]() ora me lo rileggo un po' di volte...
|
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 20:30.





















