7.5.3 Oggetti di condizione

Una variabile di condizione è sempre associata ad alcuni tipi di lock; questa può venir passata oppure creata automaticamente. (Passarne una torna utile quando alcune variabili di condizione devono condividere lo stesso lock).

Una variabile di condizione possiede i metodi acquire() e release() che chiamano i metodi corrispondenti al lock associato. Possiede anche i metodi wait(), notify() e notifyAll(). Questi tre devono essere chiamati solo quando il thread chiamante ha acquisito il lock.

Il metodo wait() rilascia il lock, e si blocca fino a che non venga risvegliato da una chiamata a notify() o notifyAll() per la stessa variabile di condizione in un altro thread. Una volta risvegliato, riacquisisce il lock e termina. È anche possibile specificare un timeout.

Il metodo notify() risveglia uno dei thread in attesa della variabile di condizione, se ce ne sono. Il metodo notifyAll() risveglia tutti i thread in attesa della variabile di condizione.

Nota: i metodi notify() e notifyAll() non rilasciano il lock; questo significa che il thread o i thread risvegliati, non restituiranno dalla loro chiamata wait() immediatamente, ma solo quando il thread che ha chiamato la notify() o la notifyAll() abbandona alla fine la proprietà del lock.

Consiglio: usando le variabili di condizione lo stile di programmazione tipico si serve dei lock per sincronizzare l'accesso ad alcuni stati condivisi; i thread che sono interessati in un particolare cambio di stato chiamano la wait() ripetutamente fino a che vedano lo stato desiderato, mentre i thread che vogliono modificare lo stato chiamano notify() o notifyAll() in modo che possa essere uno stato desiderato da uno dei thread in attesa. Per esempio, il codice seguente è una situazione generica di produttore-consumatore con buffer di capacità illimitata:

# Consuma un elemento
cv.acquire()
while not an_item_is_available():
    cv.wait()
get_an_available_item()
cv.release()

# Produce un elemento
cv.acquire()
make_an_item_available()
cv.notify()
cv.release()

Per scegliere fra notify() e notifyAll(), considerare se un cambio di stato può essere interessato a solo uno o più thread in attesa. Per esempio in una tipica situazione di produttore-consumatore, aggiungere un oggetto al buffer necessita solo del risveglio di uno dei thread consumatori.

class Condition( [lock])
Se l'argomento lock è dato e diverso da None, deve essere un oggetto Lock o RLock, ed è usato come il lock sottostante. Altrimenti, viene creato un nuovo oggetto RLock ed usato come il lock sottostante.

acquire( *args)
Acquisisce il lock sottostante. Questo metodo chiama il metodo corrispondente al lock sottostante; il valore restituito è qualsiasi cosa il metodo restituisca.

release( )
Rilascia il lock sottostante. Questo metodo chiama il metodo corrispondente al lock sottostante; non c'è alcun valore restituito.

wait( [timeout])
Aspetta fino a che non venga notificato o sopraggiunga un timeout. Deve essere chiamato solo quando il thread chiamante ha acquisito il lock.

Il metodo rilascia il lock sottostante, e si blocca fino a che non sia risvegliato da una chiamata a notify() o notifyAll() per la stessa variabile di condizione in un altro thread, o fino a che non giunga il timeout facoltativo. Una volta risvegliato o andato in timeout, riacquisisce il lock e termina.

Quando l'argomento timeout è presente e diverso da None, dovrebbe essere un numero in virgola mobile che specifica un timeout in secondi per l'operazione (o frazioni di secondo).

Quando il lock sottostante è un RLock, non viene rilasciato usando il suo metodo release(), visto che ciò potrebbe non sbloccare realmente il lock quando questo è stato acquisito più volte ricorsivamente. Invece, viene usata un'interfaccia interna della classe RLock, che lo sblocca realmente anche quando è stato acquisito ricorsivamente alcune volte. Un'altra interfaccia interna è quindi usata per ripristinare il livello di ricorsione quando il lock viene riacquisito.

notify( )
Risveglia un thread in attesa di questa condizione, se ce ne sono. Deve essere chiamata solo quando il thread chiamante ha acquisito il lock.

Questo metodo risveglia uno dei thread in attesa della variabile di condizione, se ce ne sono; non effettua alcuna operazione se non ci sono thread in attesa.

L'implementazione corrente risveglia esattamente un thread, se ce ne sono in attesa. Comunque, non è sicuro fidarsi di questo comportamento. Un'implementazione futura ottimizzata potrà occasionalmente risvegliare più di un thread.

Nota: Il thread risvegliato non restituisce realmente dalla sua chiamata wait() fino a che non possa riacquisire il lock. Visto che la notify() non rilascia il lock, lo dovrebbe fare il chiamante.

notifyAll( )
Risveglia tutti i thread in attesa di questa condizione. Questo metodo agisce come notify(), ma risveglia tutti i thread in attesa invece di uno solo.

Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.