2009-06-30 12 views
5

Mentre si passano attraverso molte risorse nella programmazione multithread, viene in genere il riferimento allo specificatore volatile. È chiaro che l'utilizzo di questa parola chiave non è un modo affidabile per ottenere la sincronizzazione tra più thread in C/C++ e Java (versioni 1.4 e precedenti). Ecco cosa liste wikipedia (senza spiegare come) usi come tipiche Questa specificazione: -Uso dello specificatore volatile in C/C++/Java

  1. consentire l'accesso ai dispositivi di memoria mappata
  2. permettono usi delle variabili tra setjmp e longjmp
  3. permettono usi di variabili nel segnale gestori
  4. occupato in attesa

posso cominciare a vedere il ruolo di questo identificatore nelle usi sopra elencati, ma dal momento che non hanno ancora un understandi completa ng di ciascuna di queste aree, non riesco a capire come si comporta esattamente questo specifier in ognuno di questi usi.

Qualcuno potrebbe spiegare?

+0

Grazie per le risposte. Qualsiasi input su come funziona esattamente volatile negli usi sopra elencati? – Ankur

risposta

6

Dato che sei interessato a questi casi di utilizzo, ti spiegherò il primo. Si noti che ciò si applica da una prospettiva c/C++, non è sicuro di come funzioni in java, anche se sospetto che in generale volatile in c/C++ e java vengano utilizzati per casi completamente diversi.

I dispositivi mappati in memoria sono periferiche alle quali il processore comunica nello stesso modo della memoria anziché tramite un bus speciale.

Supponiamo di avere una piccola luce con un timer che è mappato in memoria. Accendi la luce scrivendo 1 sul suo indirizzo di memoria & il suo timer interno conta alla rovescia per 5 secondi & spegne la luce e reimposta la posizione di memoria su 0. Ora stai sviluppando un programma in ca che deve accendere la luce dopo determinati eventi e talvolta spegnerlo prima che il contatore scada. Se si utilizza una variabile regolare (tende ad essere un puntatore o un riferimento per questo tipo di applicazione) per scrivere nella sua posizione di memoria, ci sono un certo numero di cose che potrebbero andare storte a causa delle ottimizzazioni del compilatore.

Se non si sta lavorando con quelle molte variabili e si accende la luce e poco dopo averlo spento senza altre variabili usando quel valore - a volte il compilatore eliminerà del tutto il primo compito, o in in altri casi manterrà semplicemente il valore nei registri del processore & mai scrivere in memoria. In entrambi i casi, la luce non si accende mai poiché la memoria non è mai stata modificata.

Ora pensa a un'altra situazione in cui si controlla lo stato della luce & è acceso. Qui, il valore viene estratto dalla memoria del dispositivo & conservata in un registro del processore. Ora, dopo pochi secondi, la luce si spegne da sola. Poco dopo si tenta di accendere di nuovo la luce, tuttavia dal momento che si legge che l'indirizzo di memoria & non lo ha modificato da allora, il compilatore assume che il valore sia ancora uno & pertanto non lo modifica mai, anche se attualmente è 0.

Utilizzando la parola chiave volatile, si evita che il compilatore effettui una di queste ipotesi durante la conversione del codice in codice macchina & assicurando che tutte quelle operazioni specifiche vengano eseguite rigorosamente come scritto dal programmatore. Questo è essenziale per i dispositivi mappati in memoria principalmente perché la posizione della memoria non viene modificata rigorosamente dal processore. Per questi stessi motivi, i sistemi multiprocessore con memoria condivisa spesso richiedono pratiche simili quando operano su uno spazio di memoria comune.

+0

Grazie DB. Ottimo esempio. – Ankur

2

C'è una buona spiegazione qui: http://en.wikipedia.org/wiki/Volatile_variable ma leggermente semplificato dice al compilatore che non deve assumere che la variabile non è accessibile da qualcun altro e che è fatale ottimizzarlo in un registratore e aggiornare solo il registro e non la memoria reale.

+0

È diverso in C e C++ rispetto a Java, né sorprendentemente. per esempio in Java, se il compilatore può determinare che un volatile è accessibile solo da un singolo thread, può essere trattato come se non fosse volatile. –

+0

Corretto, penso che lo menzionino anche in wikipedia. Lo scopo dello specificatore è tuttavia ancora più o meno lo stesso. – Fredrik

-1

La variabile volatile deve essere utilizzata quando è possibile accedere a tale variabile da più thread e si desidera che in ogni istruzione il codice debba ottenere il valore aggiornato di tale variabile.

I compilatori generalmente ottimizzano il codice e memorizzano le variabili nel registro anziché prelevare dalla memoria ogni volta se non lo vedono nessuno lo ha aggiornato.

Ma utilizzando volatile è possibile forzare il compilatore a ottenere il valore aggiornato ogni volta.

+1

Questo non è corretto. 'volatile' sta praticamente dicendo al compilatore che "qualcosa fuori dal tuo controllo potrebbe modificare questa variabile, quindi non ottimizzarla". Non hai davvero altre garanzie su ciò che accadrà. I link nelle altre risposte spiegano perché è sbagliato usare "volatile" mentre descrivi. –

+0

Considera le cache della cpu. La macchina astratta C non conosce le cache della cpu. Per questo, un valore può essere in memoria se è appena nella cache della cpu di * one * cpu, per esempio. Per le app con più thread, l'utilizzo di una cache di questo tipo come "memoria" non è valido. –

+0

@Richard: concordato.lo specificatore volatile non dovrebbe mai essere usato come mezzo per ottenere la sincronizzazione tra più thread. – Ankur

12

La tua domanda è tecnicamente nota come "una lattina di vermi"! Per c/C++ (non riesco a commentare java)
Si può rudemente riepilogare volatile come una direttiva al compilatore per dire "per favore non ottimizzarlo" ma c'è un sacco di argomenti tra i professionisti, per se è
a) At all useful for kernel level code < -Edit chiarito basata sui commenti
b) Even implemented correctly by most compilers.

Inoltre, non mai utilizzarlo per la programmazione multi-threaded e here's a very good explanation as to why

= Edit = interessante, per per quello che vale. Dennis Ritchie era contro i suoi dettagli di inclusione (come pure cost) here

+1

Si noti che i collegamenti del documento "Tutto utile" sulla carta del kernel Linux. Quel documento parla di volatile utilizzato nel kernel di Linux. In fondo, danno ancora buoni usi di volatile, che non riguardano più thread. Volatile ha senso ed è buono lì (se il compilatore tratta volatile correttamente), come in un ciclo occupato in cui la variabile viene aggiornata in un gestore di interrupt della stessa CPU. Il compilatore non può sapere se non dovrebbe ottimizzare una lettura. Il tuo punto fa sembrare che "volatile" sia una schifezza completa (anche se so che potresti non volerlo) :) –

+0

@litb Yep fair point. Renderò questo punto più chiaro. Non direi che è stata una schifezza totale, ho solo pensato di far notare che non è proprio così chiaro – zebrabox

+0

Esattamente quello che stavo per dire. Un chiarimento è che l'ottimizzazione di solito memorizzava il valore in un registro, riutilizzandolo da lì. Volatile forza il codice a rileggere il valore dalla memoria su ogni lettura e non si basa su un valore memorizzato nella cache nel registro. Ora i compilatori fanno un sacco di ottimizzazioni incluse le dichiarazioni di riscrittura e il loro ordine, quindi semplicemente rileggere un valore dalla memoria non è sufficiente se si fa affidamento sull'ordine di aggiornamento in altri thread, per affrontare questo richiede barriere di memoria. – iain

4

Ho trovato questo articolo DDJ di Herb Sutter molto interessante, specialmente quanto volatile è trattato in C++, Java e C# .NET.

Dr.Dobbs volatile vs. volatile

+0

Grazie ovanes. Bene. – Ankur

0

E 'stato un po' che ho fatto C++ e io davvero non ricordo la definizione di volatine in quella lingua. Ma le specifiche del linguaggio Java specificano specificamente che lo scopo di volatile è quello di facilitare l'accesso multi-thread a una variabile.Citazione: "Un campo può essere dichiarato volatile, nel qual caso il modello di memoria Java (§17) assicura che tutti i thread vedano un valore coerente per la variabile." Continuano a dire che i riferimenti ai valori volatili sono garantiti per essere soddisfatti nell'ordine in cui sono specificati nel codice, cioè se dichiari i e j volatile e poi scrivi "++ i; ++ j", quindi i sarà, infatti, sempre incrementato prima di j.

L'unica volta che ho richiamato l'utilizzo di un volatile in Java è stato quando ho avuto un thread che probabilmente impostava un flag di annullamento e un altro thread che passava attraverso una grande operazione e ogni volta attraverso il ciclo controllando il flag di cancellazione. Questo ha funzionato davvero come mi aspettavo.

Sono d'accordo che "volatile" ha un'utilità molto limitata. La maggior parte del multi-threading richiede "sincronizzati" ad un certo punto. Ma "limitato" e "nessuno" non sono la stessa cosa. La funzione coseno ha un'utilità molto limitata anche nella maggior parte delle applicazioni aziendali. Ma quando ne hai bisogno, wow, questo ti risparmia un sacco di problemi.

1

La parola chiave volatile è comparsa molto tempo fa in C, e ciò che fa è fondamentalmente "disattivare" alcune ottimizzazioni del compilatore che presuppongono che una variabile non è stata modificata esplicitamente, non è stata affatto modificata. La sua utilità principale quei giorni era dichiarare le variabili che sarebbero state cambiate dai gestori di interrupt.Io, ad esempio, l'ho usato una volta (alla fine degli anni '80) per una variabile globale contenente la posizione del cursore del mouse. La posizione è stata modificata da un interrupt, e senza volatile il programma principale a volte non avrebbe rilevato i suoi cambiamenti perché il compilatore ottimizzava l'accesso variabile, pensando che non fosse necessario.

Oggi questi usi sono, in generale, obsoleti (a meno che non si scriva codice OS di basso livello) ma ci sono ancora alcune rare situazioni in cui è utile la volatile (molto raro in effetti - io, ad esempio, probabilmente non ho usato per gli ultimi 7 anni).

Ma per la programmazione multithread è completamente sconsigliato. Il problema è che non proteggerà per l'accesso simultaneo tra thread, rimuoverà solo le ottimizzazioni che impedirebbero il suo 'refresh' nello stesso thread. Non è stato progettato per l'utilizzo in ambienti con multithreading. Se sei in Java, usa sincronizzato. Se sei in C++, usa qualche libreria di sincronizzazione, come pthreads o Boost.Threads (o, meglio ancora, usa le nuove librerie di thread C++ 0X, se puoi).

2

variabili volatili sono utili in Java (almeno dal Java 5.0 dove il loro behaviour changed), come dice Brian Goetz nel suo libro "Java Concurrency in Practice" (JCIP) - il libro essenziale sull'argomento (pg 37):

per garantire che gli aggiornamenti di una variabile vengono propagate prevedibilmente ad altri discussioni

sincronizzazione esplicita può anche raggiungere questo obiettivo, ma spesso non sempre desidera bloccare un valore. Doppia chiusura Controllato è il classico esempio di questo (copiato da Wikipedia):

// Works with acquire/release semantics for volatile 
// Broken under Java 1.4 and earlier semantics for volatile 
class Foo { 
    private volatile Helper helper = null; 
    public Helper getHelper() { 
     if (helper == null) { 
      synchronized(this) { 
       if (null == helper) 
        helper = new Helper(); 
      } 
     } 
     return helper; 
    } 

    // other functions and members... 
} 

Questo non funzionerebbe se l'aiuto non è stato volatile.

variabili volatili possono anche essere utilizzati per implementare non bloccaggio Datastructures concomitanti, come java.util.concurrent.ConcurrentHashMap (che supporta aggiornamenti concorrenti e accesso senza bloccare - vedere il codice sorgente JDK per l'uso di sostanze volatili).

JCIP ha una bella discussione sul blocco a doppio controllo, sulle variabili volatili e sulla concorrenza Java in generale. Vale la pena leggere anche "Effective Java", seconda edizione di Joshua Bloch.

Si noti inoltre che le variabili atomiche sono supportate in Java nel pacchetto java.util.concurrent.atomic. Ciò consente di rendere visibili le modifiche ai valori attraverso thread/processori in modo simile alle variabili volatili, ma consente anche di eseguire operazioni "Confronta e imposta", il che significa che alcuni tipi aggiuntivi di operazioni simultanee possono essere eseguiti senza bloccare.

Problemi correlati