2012-05-08 16 views
14

Su una macchina x86 multicore, un thread in esecuzione su core1 incrementa una variabile intera a allo stesso tempo il thread sul core 2 lo incrementa anche. Dato che il valore iniziale di a era 0, sarebbe sempre 2 alla fine? O potrebbe avere qualche altro valore? Si supponga che sia dichiarato come volatile e che non si utilizzino variabili atomiche (come atomico <> di C++ e operazioni atomiche incorporate in gcc).È incrementale un numero intero atomico in x86?

Se il valore della a sarebbe effettivamente essere sempre 2 in tal caso, vuol significare che una long int in x86-64 avrebbe anche la stessa proprietà, vale a dire, a saranno sempre 2 alla fine?

+4

a meno che non si utilizzi un tipo atomico speciale, l'incremento è in genere tre operazioni separate. Carica, incrementa, quindi archivia. –

+4

'volatile' non ti dà accesso atomico. –

+3

@CatPlusPlus è il tuo nome un'operazione atomica? : P – MByD

risposta

24

L'istruzione della macchina con memoria incrementale su un X86 è atomica solo se la si utilizza con un prefisso LOCK.

x ++ in C e C++ non ha un comportamento atomico. Se si eseguono incrementi sbloccati, a causa di razze in cui il processore sta leggendo e scrivendo X, se due processori separati tentano un incremento, si può finire con un solo incremento o entrambi (il secondo processore potrebbe aver letto il valore iniziale, incrementato esso e lo riscrive dopo che il primo ha scritto i suoi risultati indietro).

Credo che C++ 11 offra incrementi atomici e la maggior parte dei compilatori di fornitori ha un modo idiomatico di causare un incremento atomico di alcuni tipi di interi incorporati (in genere int e long); consultare il manuale di riferimento del compilatore.

Se si desidera incrementare un "valore grande" (ad esempio un numero intero multiprecisione), è necessario farlo utilizzando un meccanismo di blocco standard come un semaforo.

Nota che è necessario preoccuparsi dell'atomico letture, anche. Sull'86, la lettura di un valore a 32 o 64 bit è atomica se è allineata a 64 bit. Ciò non sarà vero per un "grande valore"; di nuovo avrai bisogno di un blocco standard.

+0

Il prefisso LOCK garantisce anche le necessarie recinzioni? (Penso di sì, ma non ne sono sicuro.) –

+0

Sì. .......... –

4

Non è garantito. È possibile utilizzare l'istruzione lock xadd per ottenere lo stesso effetto, oppure utilizzare C++ std::atomic oppure utilizzare #pragma omp atomic o un numero qualsiasi di altre soluzioni di concorrenza che sono state scritte per risparmiarvi la fatica di reinventare la ruota.

7

Ecco un prova non è atomica in una particolare implementazione (GCC), come si può vedere (?), Gcc genera codice che

  1. carica il valore dalla memoria ad un registro
  2. incrementi il contenuto del registro
  3. salva il registro in memoria.

Questo è molto lontano dall'essere atomico.

$ cat t.c 
volatile int a; 

void func(void) 
{ 
    a++; 
} 
[19:51:52 0 ~] $ gcc -O2 -c t.c 
[19:51:55 0 ~] $ objdump -d t.o 

t.o:  file format elf32-i386 


Disassembly of section .text: 

00000000 <func>: 
    0: a1 00 00 00 00   mov 0x0,%eax 
    5: 83 c0 01    add $0x1,%eax 
    8: a3 00 00 00 00   mov %eax,0x0 
    d: c3      ret 

Non lasciatevi ingannare dal 0x0 nell'istruzione mov, c'è spazio per 4 byte lì, e il linker compilare l'indirizzo di memoria risultante per a lì quando questo file oggetto è collegato.

+0

Questo è divertente, in realtà si ottiene solo un carico/add/store separato quando 'a' è' volatile', altrimenti gcc usa un read-modify-write (a meno che non si sintonizzi per [i586 (pentium)] (https://godbolt.org/g/3K8zfu)). Naturalmente, se il codice circostante utilizza il valore di num ++, molto probabilmente sarà fatto con istruzioni separate per lasciare il risultato in un registro. Ho menzionato questo nella mia risposta su [una versione non-'volatile' la domanda] (http://stackoverflow.com/questions/39393850/can-num-be-atomic-for-int-num/39396999#39396999). –

+0

@PaterCordes Senza volatile, gcc potrebbe generare una singola istruzione 'addl', sebbene non sia atomica a meno che non si usi anche il prefisso' lock'. – nos

+0

: P Il secondo collegamento nel mio commento precedente è alla mia risposta che spiega esattamente (in termini di protocollo MESI, ecc.) Perché "addl $ 1, num' non è atomico (eccetto su un sistema uniprocessore se non lo facciamo include gli osservatori DMA) e perché è con 'lock'. –

7

Dal momento che nessuno ha risposto alla tua domanda attuale e invece si stanno dimostrando come farlo in un modo che funziona sempre:

discussione il valore 1 carichi di 0

Discussione valore 2 carichi di 0

Discussione incrementi di 1 un negozi 1

thread 2 incrementa la sua copia del registro locale del valore e negozi 1.

Come puoi vedere che il risultato finale è un valore uguale a 1 e non 2. Non sarà sempre 2 alla fine.

Problemi correlati