2010-02-26 17 views
6

Sto scrivendo un'app C++.variabili di multithreading volatile C++

Ho una variabile di classe a cui sta scrivendo più di un thread.

In C++, tutto ciò che può essere modificato senza che il compilatore "capisca" di essere cambiato deve essere contrassegnato come volatile? Quindi se il mio codice è multi-thread e un thread può scrivere su var mentre un altro lo legge, devo contrassegnare il var volaltile?

[non ho una condizione di competizione dal momento che sto contando sulla scrittura per interi essere atomica]

Grazie!

+9

Cosa ti fa pensare che le scritture saranno atomiche? – bmargulies

+4

Non sono esperto di multithreading, ma implementerei sempre il locking su qualsiasi risorsa condivisa ... –

+0

Ho eseguito questo tipo di operazioni su un processore single-core PowerPC incorporato e funziona in modo affidabile. Meglio non fare alcun read-modify-write (come '++ sharedInt') se è possibile che due thread abbiano accesso in scrittura. (In realtà, se due thread possono scrivere, è probabilmente utile solo se limiti ** quando ** possono scrivere. Ad esempio, il thread A può cambiare 'sharedInt' da 0 a 1 mentre il thread B può cambiarlo da 1 a 0.) – Dan

risposta

3

volatile istruire il compilatore a non ottimizzare "intuizione" di un valore o di un utilizzo variabile poiché potrebbe essere ottimizzato "dall'esterno".

volatile non fornirà comunque alcuna sincronizzazione e la tua ipotesi di scrittura su int atomica è quasi realistica!

Suppongo che avremmo bisogno di vedere un po 'di utilizzo per sapere se volatile è necessario nel tuo caso (o controllare il comportamento del tuo programma) o, soprattutto, se vedi una sorta di sincronizzazione.

+0

Questo NON è vero, quando si costruiscono applicazioni/algoritmi HPC di solito si è abbastanza consapevoli dell'architettura con cui si lavorerà. E quindi non aggiungerai un blocco inutile, se non ne hai bisogno. – Ben

+0

Beh, hum, almeno il mio punto di vista. – Ben

+0

Ho sempre pensato che scrivere un int fosse un'opzione atomica (almeno sulla CPU x86). Avete delle buone documentazioni sulle operazioni atomiche? –

0

Senza il blocco si possono ancora ottenere riordinamenti "impossibili" eseguiti dal compilatore o dal processore. E non c'è alcuna garanzia che le scritture su inti siano atomiche.

Sarebbe meglio usare un bloccaggio adeguato.

13

C++ non ha ancora nessuna disposizione per il multithreading. In pratica, volatile non fa ciò che intendi (è stato progettato per hardware con memoria indirizzata e mentre i due problemi sono simili sono abbastanza diversi da rendere volatile la cosa giusta - nota che volatile è stato utilizzato in altri linguaggio per usi in contesti mt).

Quindi, se si desidera scrivere un oggetto in un thread e leggerlo in un altro, è necessario utilizzare le funzionalità di sincronizzazione necessarie per l'implementazione quando ne ha bisogno. Per quello che conosco, la volatilità non gioca alcun ruolo in questo.

FYI, il prossimo standard prenderà in considerazione MT e volatile non avrà alcun ruolo in questo. In modo che non cambierà. Avrai solo condizioni standard definite in cui è necessaria la sincronizzazione e un modo standard definito per raggiungerle.

+0

"In pratica, volatile non fa quello che intendi". Che cosa?? Volatile è progettato per prevenire le ottimizzazioni del compilatore su una variabile. È altamente raccomandato per la programmazione multithread. Non tentare la programmazione multithread senza volatile. Il tuo programma potrebbe funzionare bene per anni e improvvisamente crollare dal nulla. –

+0

@annoying_squid, vedere http://stackoverflow.com/a/2485177/136208. A partire da C++ 11, 'std :: atomic <>' è probabilmente il modo per ottenere ciò che tu sei volatile fornirebbe. – AProgrammer

-4

Volatile risolverà il problema, ad es. garantirà la coerenza tra tutte le cache del sistema. Tuttavia sarà inefficienza dal momento che aggiornerà la variabile in memoria per ogni accesso R o W. Si potrebbe prendere in considerazione l'utilizzo di una barriera di memoria, solo quando è necessario, invece. Se si lavora con o gcc/ICC hanno sguardo sulla sincronizzazione built-in: http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

EDIT (per lo più su commenti PM100): Capisco che le mie convinzioni non sono un punto di riferimento così ho trovato qualcosa di citare :)

La parola chiave volatile è stata concepita per impedire ottimizzazioni del compilatore che potrebbero rendere il codice errato in presenza di determinati eventi asincroni.Ad esempio,se si dichiara una variabile primitiva come volatile, il compilatore non è consentito di memorizzare nella cache in un registro

Da Dr Dobb's

Più interessante:

campi volatili sono linearizzabile. Leggere un campo volatile è come acquistare una serratura; la memoria di lavoro viene invalidata e il valore corrente del campo volatile viene riletto dalla memoria. Scrivere un campo volatile è come liberare un blocco: il campo volatile viene immediatamente riscritto in memoria. (questo è tutto di coerenza, non si tratta di atomicità)

da L'arte della programmazione multiprocessore, Maurice Herlihy & Nir Shavit

blocco contiene codice di sincronizzazione di memoria, se non si blocca, è necessario fare qualcosa e usare parole chiave volatili è probabilmente la cosa più semplice che puoi fare (anche se è stato progettato per dispositivi esterni con memoria legata allo spazio degli indirizzi, non è il punto qui)

+0

Perché questa risposta è downvoted? – anon

+1

perché è sbagliato. volatile non ha nulla a che fare con i cache della memoria. – pm100

+0

@ pm100, non è stato progettato per questo sono d'accordo, ma ha a che fare con le cache, vedi la mia modifica per favore. – Ben

4

Sì, volatile è il minimo assoluto che tu " Ho bisogno. Assicura che il generatore di codice non generi codice che memorizza la variabile in un registro e esegue sempre letture e scritture da/verso la memoria. La maggior parte dei generatori di codice può fornire garanzie di atomicità su variabili che hanno le stesse dimensioni della parola CPU nativa, assicureranno che l'indirizzo di memoria sia allineato in modo tale che la variabile non possa scavalcare un limite di cache-line.

Questo non è tuttavia un contratto molto forte per le moderne CPU multi-core. Volatile fa non promettere che un altro thread che gira su un altro core può vedere gli aggiornamenti alla variabile. Ciò richiede una barriera di memoria, di solito un'istruzione che svuota la cache della CPU. Se non si fornisce una barriera, il filo continuerà a funzionare fino a quando non si verificherà un flusso naturale. Ciò accadrà alla fine, lo scheduler del thread è destinato a fornirne uno. Questo può richiedere millisecondi.

Una volta che ci si è occupati di dettagli come questo, alla fine si sarà reinventato una variabile di condizione (nota anche come evento) che probabilmente non sarà più veloce di quella fornita da una libreria di threading. O altrettanto testato. Non inventare il tuo, il threading è abbastanza difficile da essere corretto, non hai bisogno del FUD di non essere sicuro che i primitivi di base siano solidi.

+2

Volatile non è il minimo assoluto. È ben al di sotto del minimo. Il minimo dovrebbe anche impedire il riordino di lettura/scrittura attorno alla variabile condivisa. – jalf

+0

@jalf - quante gradazioni sotto "minimo assoluto" ti preoccupi di considerare? –

+0

Solo uno: "sotto di esso";) – jalf

1

Penso che lo standard volatile si applichi davvero solo alla lettura, in particolare alla lettura di registri I/O mappati in memoria.

Può essere utilizzato per indicare al compilatore di non dare per scontato che una volta che ha letto da una posizione di memoria che il valore non cambierà:

while (*p) 
{ 
    // ... 
} 

Nel codice di cui sopra, se *p non viene scritto all'interno del ciclo, il compilatore potrebbe decidere di spostare la lettura di fuori del ciclo, più simile a questo:

cached_p=*p 
while (cached_p) 
{ 
    // ... 
} 

Se p è un puntatore ad una porta I/O mappato in memoria, si vorrebbe la prima versione in cui il la porta viene controllata prima che il ciclo venga inserito sempre y tempo

Se p è un puntatore alla memoria in un'app multi-thread, non è ancora garantito che le scritture siano atomiche.

+6

Non si tratta solo di lettura: "per (i = 0; i <10; ++ i) {j = i;} può essere sostituito con j = 10; quando j non è volatile. – stefaanv