2010-05-23 10 views
9

Una domanda veloce di cui mi sono interrogato da tempo; La CPU assegna i valori atomicamente, oppure, è bit per bit (ad esempio, un intero a 32 bit).
Se è un bit alla volta, un altro thread che accede a questa posizione esatta ottiene una "parte" del valore da assegnare?Una CPU assegna un valore atomicamente alla memoria?

Pensa a questo:
Ho due thread e una variabile "unsigned int" condivisa (chiamiamola "g_uiVal").
Entrambi i fili si avvolgono.
On sta stampando "g_uiVal" con printf ("% u \ n", g_uiVal).
Il secondo aumenta semplicemente questo numero.
Il filo di stampa stampa mai qualcosa che non è totalmente o parte del valore "g_uiVal"?

in codice:

unsigned int g_uiVal; 

void thread_writer() 
{ 
g_uiVal++; 
} 
void thread_reader() 
{ 
while(1) 
    printf("%u\n", g_uiVal); 
} 

risposta

5

Dipende dalle larghezze del bus della CPU e della memoria. In un contesto PC, con qualcosa di diverso da una CPU molto antica, gli accessi fino a 32 bit di accesso sono atomici; Gli accessi a 64 bit possono o non possono essere. Nello spazio incorporato, molte (la maggior parte?) CPU hanno una larghezza di 32 bit e non è previsto nulla di più ampio, quindi il tuo int64_t è garantito per essere non atomico.

+0

È possibile accedere ai valori a 32 bit che attraversano le linee della cache in questi giorni? –

+0

@Lasse: molti processori desktop moderni consentono letture e scritture non allineate, ma con una significativa riduzione delle prestazioni. Le CPU meno recenti o più piccole (ad esempio per i dispositivi incorporati) tendono a non farlo. Per qualche tempo questo è stato diviso tra processori CISC (tendenzialmente per supportare letture e scritture non allineate) e processori RISC (non), ma qui le distinzioni si stanno attenuando. – leander

+0

I microcontrollori a 8, 16 e 32 bit sono tutti comuni. Su un AVR (con carichi e depositi a 8 bit) è possibile avere un altro ISR (routine di servizio di interrupt) o un altro thread (se si sta eseguendo un sistema operativo multitasking preempibile) scrivere parte della variabile (o solo leggere parte del cambia quello fatto il thread precedente). – nategoose

3

credo che l'unica risposta corretta è "dipende". Su cosa potresti chiedere?

Bene per gli avviatori che CPU. Ma anche alcune CPU sono atomiche per scrivere valori di larghezza di parole, ma solo quando sono allineate. In realtà non è qualcosa che puoi garantire a livello di linguaggio C.

Molti compilatori offrono "elementi intrinseci" per emettere operazioni atomiche corrette. Queste sono estensioni che agiscono come funzioni, ma emettono il codice corretto per l'architettura di destinazione per ottenere le operazioni atomiche necessarie. Ad esempio: http://gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html

0

Per aggiungere a quanto è stato detto finora, un altro potenziale problema è il caching. Le CPU tendono a funzionare con la cache di memoria locale (su die) che può o meno essere scaricata immediatamente nella memoria principale. Se la scatola ha più di una CPU, è possibile che un'altra CPU non veda le modifiche per qualche tempo dopo che la CPU di modifica le ha fatte - a meno che non ci sia un comando di sincronizzazione che informa tutte le CPU che dovrebbero sincronizzare le loro cache on-die. Come puoi immaginare, una simile sincronizzazione può rallentare notevolmente l'elaborazione.

+0

Ma in questo caso, poiché non c'è sincronizzazione tra consumatore e produttore, in realtà non cambia il comportamento. Certo, il consumatore poteva leggere un vecchio valore, ma non sarebbe possibile dire se fosse dovuto a cache on-die non sincronizzate o semplicemente alla programmazione. Quello che sto ottenendo è che il consumatore non leggerà mai un valore parzialmente scritto a causa di cache non sincronizzate –

+0

Tutto dipende da cosa è previsto. Se l'intenzione è quella di produrre valori univoci - bene, questo problema può introdurre duplicati – mfeingold

0

Non dimenticare che il compilatore presuppone il single-thread durante l'ottimizzazione e l'intera cosa potrebbe andare via.

+0

anche se è una variabile globale che è chiaramente in uso in altre funzioni? Dubito che qualsiasi compilatore sarebbe * che * maleducato :) –

+0

@Isak Savo: variabile globale non volatile statica (non esterna)? certo, perché no? Contrassegna tutte le variabili utilizzate per il controllo della concorrenza come volatili, ciò impedisce le ottimizzazioni del compilatore correlate a queste variabili. – liori

+0

@liori: Ma anche in una singola applicazione con thread, questo è un codice perfettamente valido.Bad architettura a parte, non c'è niente di sbagliato con avere una funzione modificare le variabili globali senza utilizzare il risultato stesso. –

1

Hai detto "bit per bit" nella tua domanda. Non credo che nessuna architettura funzioni un po 'alla volta, tranne che con alcuni bus specializzati per il protocollo seriale. Le operazioni di lettura/scrittura della memoria standard vengono eseguite con 8, 16, 32 o 64 bit di granularità. Quindi è POSSIBILE che l'operazione nel tuo esempio sia atomica.

Tuttavia, la risposta dipende fortemente dalla piattaforma.

  • Dipende dalle capacità della CPU. L'hardware può eseguire un'operazione atomica a 32 bit ? Ecco un suggerimento: se la variabile su cui stai lavorando è maggiore di rispetto alla dimensione del registro nativo (ad esempio int a 64 bit su un sistema a 32 bit), è sicuramente NON atomico.
  • Dipende dal modo in cui il compilatore genera il codice macchina. Potrebbe aver trasformato l'accesso della variabile a 32 bit in 4 letture di memoria a 8 bit.
  • Diventa complicato se l'indirizzo di ciò che si sta accedendo non è allineato attraverso il limite di parola naturale di una macchina. È possibile colpire un errore di cache o pagina.

È MOLTO POSSIBILE che si visualizzi un valore corrotto o imprevisto utilizzando l'esempio di codice che è stato pubblicato.

La piattaforma probabilmente fornisce un metodo per eseguire operazioni atomiche. Nel caso di una piattaforma Windows, è tramite lo Interlocked functions. Nel caso di Linux/Unix, guarda lo atomic_t type.

0

POSIX definisce il tipo speciale sig_atomic_t che garantisce che le scritture siano atomiche rispetto ai segnali, che lo rendono anche atomico dal punto di vista di altri thread come desiderato. Non definiscono in modo specifico un tipo di cross-thread atomico come questo, poiché si presuppone che la comunicazione dei thread sia mediata da mutex o altre primitive di sincronizzazione.

0

Considerando i moderni microprocessori (e ignorando i microcontrollori), l'assegnazione a 32 bit è atomica, non bit per bit.

Tuttavia, ora completamente fuori dall'argomento della tua domanda ... il thread di stampa potrebbe ancora stampare qualcosa che non è previsto a causa della mancanza di sincronizzazione in questo esempio, naturalmente, a causa del riordino delle istruzioni e di più core ciascuno con il loro possedere la copia di g_uiVal nelle loro cache.

Problemi correlati