2015-07-08 17 views
32

C'è stata un'aggiunta nel recente Clojure 1.7 release: volatile!Che cos'è Clojure volatile?

volatile è già utilizzato in molte lingue, tra cui Java, ma quali sono la semantica in Clojure?

Cosa fa? Quando è utile?

+1

Guardando [l'origine] (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Volatile.java) è solo un involucro attorno a una variabile mutabile. – Lee

+1

forse avvisa quando viene utilizzato nella transazione, simile a 'io!' read-up: http://dev.clojure.org/jira/browse/CLJ-1512 – birdspider

+1

'volatile!' Non è thread-safe, quindi dovrebbe essere utilizzato in un solo thread, come nel caso dei trasduttori a thread singolo. – claj

risposta

47

La nuova volatile è vicino come un vero e proprio "variabile" (come è da molti altri linguaggi di programmazione) come ottiene per clojure.

Da the announcement:

ci sono una nuova serie di funzioni (volatile!, vswap!, vreset!, volatile?) per creare e utilizzare "scatole" volatili di mantenere lo stato di trasduttori con stato. I volatili sono più veloci degli atomi ma rinunciano alle garanzie di atomicità, quindi dovrebbero essere usati solo con isolamento del filo.

Per esempio, è possibile impostare/ottenere e aggiornarli, proprio come si farebbe con una variabile in C. L'unica aggiunta (e da qui il nome) è la parola chiave per volatilethe actual java object.

Questo serve per impedire l'ottimizzazione della JVM e si assicura che legga la posizione di memoria ogni volta che si accede. Da the JIRA ticket:

Clojure bisogno di una variante più veloce di Atomo di gestione di stato all'interno trasduttori. Cioè, gli atomi fanno il lavoro, ma forniscono un po 'troppa capacità ai fini dei trasduttori. In particolare, la semantica di confronto e scambio degli atomi aggiunge troppi sovraccarichi. Pertanto, è stato determinato che un semplice tipo di riferimento volatile avrebbe funzionato per garantire la propagazione di base del suo valore ad altri thread e letture dell'ultima scrittura da qualsiasi altro thread. Mentre gli aggiornamenti sono soggetti a condizioni di gara, l'accesso è controllato dalle garanzie JVM.

Panoramica della soluzione: creare un tipo concreto in Java, simile a clojure.lang.Box, ma volatili all'interno supporta IDeref, ma non gli orologi ecc

Questo significa, un volatile! può ancora essere accessibile da più thread (che è necessario per i trasduttori) ma non consente di essere modificato da da questi thread allo stesso tempo poiché non fornisce aggiornamenti atomici.

La semantica di ciò volatile fa è molto ben spiegato in a java answer:

ci sono due aspetti di filo di sicurezza: (1) controllo di esecuzione, e (2) la visibilità memoria. Il primo ha a che fare con il controllo quando il codice viene eseguito (compreso l'ordine in cui le istruzioni vengono eseguite) e se può essere eseguito contemporaneamente, e il secondo a che fare quando gli effetti in memoria di ciò che è stato fatto sono visibili ad altri thread. Poiché ogni CPU ha diversi livelli di cache tra la memoria principale e quella principale, i thread in esecuzione su CPU o core differenti possono vedere la "memoria" in modo diverso in qualsiasi momento dato che i thread sono autorizzati ad ottenere e lavorare su copie private della memoria principale.

Ora vediamo perché non usare var-set o transients:

volatile vs var-set

Rich Hickey didn't want to give truly mutable variables:

Senza locali mutevoli, le persone sono costrette a usare ripetersi, un funzionale costrutto ciclico. Anche se all'inizio questo può sembrare strano, è uguale a succinto come loop con mutazione, ei pattern risultanti possono essere riutilizzati altrove in Clojure, vale a dire ricorrere, ridurre, modificare, commutare ecc. sono tutti (logicamente) molto simili. [...] In ogni caso, Vars sono disponibili per l'uso quando appropriato.

E così la creazione di with-local-vars, var-set ecc .. Il problema di queste è che sono verovars e la doc string di var-set ti dice:

Il var deve essere filettatura legato localmente.

Questa ovviamente non è un'opzione per core.async che può essere eseguita su thread diversi. Sono anche molto più lenti perché fanno tutti quegli assegni.

perché non utilizzare i transitori

Transients sono simili, nel senso che non consentono l'accesso concorrente e ottimizzano mutando una struttura di dati. Il problema è che il transitorio funziona solo con la raccolta che implementa IEditableCollection. Cioè sono semplicemente per evitare costose rappresentazioni intermedie delle strutture di raccolta dati. Ricorda inoltre che i transienti non sono bloccati in posizione e hai ancora bisogno di una posizione di memoria per memorizzare il transitorio effettivo. volatili sono spesso utilizzati per tenere semplicemente una bandiera o il valore dell'ultimo elemento (vedi partition-by per esempio)

Sommario:

di volatili sono altro che un wrapper Java di volatili e hanno quindi le stesse identiche semantica . Non condividerli mai. Usali solo molto attentamente.

+1

risposta superba che anticipava le domande successive sollevate da una risposta concisa ma tecnicamente corretta. –

19

I volatili sono un "atomo più veloce" senza garanzie di atomicità. Erano introduced poiché gli atomi erano considerati troppo lenti per mantenere lo stato nei trasduttori.

sono presenti una nuova serie di funzioni (volatile!, vswap!, vreset!, volatile?) per creare e utilizzare "scatole" volatili di mantenere lo stato di trasduttori con stato. Volatili vengono velocemente di atomi ma rinunciare garanzie atomicità quindi devono essere utilizzati solo con filo isolamento