2014-09-03 14 views
6

Sono confuso circa il funzionamento atomica su C++ 11,C++ 11 quando incrementa la variabile atomica e la assegna ad un altro valore, è un'operazione atomica?

So che il atomica sé incremento variabile è un'operazione atomica,

ma io uso il compito di altro valore, solo dubitarne.

il codice, proprio come:

//.... 
static std::atomic<int> i; // global variable 
//.... 
// in the thread 
int id = ++i; 

quando si utilizza l'assegnazione a diversi thread, è il valore unico id?

il codice di prova:

#include <thread> 
#include <mutex> 
#include <atomic> 
#include <iostream> 

class A { 
public: 
    static int idGenerator; 
    static std::mutex m; 
    A() { 
     // i know this operation will keep the id_ is unique 
     std::lock_guard<std::mutex> lock(m); 
     id_ = ++idGenerator; 
    } 
    void F(std::string name) { 
     std::cout << name << " " << id_ << std::endl; 
    } 
private: 
    int id_; 
}; 
int A::idGenerator = 0; 
std::mutex A::m; 

class B { 
public: 
    static int idGenerator; 
    B() { 
     // after self increment, is the assignment atomic? 
     id_ = (++idGenerator); 
    } 
    void F(std::string name) { 
     std::cout << name << " " << id_.load() << std::endl; 
    } 
private: 
    std::atomic<int> id_; 
}; 
int B::idGenerator = 0; 


void funcA() { 
    A a2; 
    a2.F("a2"); 
} 

void funcB() { 
    B b2; 
    b2.F("b2"); 
} 

int main() { 
    A a1; 
    B b1; 
    std::thread t1(&funcA); 
    std::thread t2(&funcB); 
    a1.F("a1"); 
    b1.F("b1"); 

    t1.join(); 
    t2.join(); 
    return 0; 
} 

ci sono tre fili,

Una classe di utilizzo lock_guard mantengono unico.

classe B basta usare operazione atomica, e assegnare alla variabile

+0

Vedere l'API 'std :: atomic :: fetch_add' che copre le due operazioni in un'unità atomica. –

+0

Un'assegnazione a una variabile atomica (o un'operazione di incremento) è, beh, atomica. Non è garantito che l'assegnazione a qualsiasi altra variabile sia atomica. Tuttavia, l'atomicità da sola non è sufficiente in tutti gli scenari con accesso concorrente per renderla "thread-safe". Inoltre, osservando il codice in classe 'B', sembra che si desideri rendere la variabile membro id' atomgenerator', invece la variabile membro 'id_'. – CouchDeveloper

+0

sto leggendo il codice crtmpserver, e ogni connessione con la sua unica classe iohandler, l'id del gestore personale è generata dal generatore statico. crtmpserver è Single Process, se provo ad aggiungere il supporto multithread, l'id dovrebbe rimanere unico come prima –

risposta

0

kaka_ace,

Purtroppo nel caso che hai fornito, non è atomica.

Ecco il motivo per cui l'operazione di pre-incremento è atomico, guarda l'assembly generato:

add %l0,1,%l0 

(possono variare un po 'a seconda dell'armatura utilizzata)

Ma questo è tutto. 1 operazione. Ecco perché è atomico.

Quando si sta assegnando un pre-incremento a una variabile locale, che è almeno due istruzioni:

add %l0,1,%l0 
st l0, [%fp-4] 

che genera almeno due istruzioni, e quindi non è più atomica.

Per favore fatemi sapere se avete domande!

+1

Il tuo 'add% l0,1,% l0' mostra il codice che usa un registro CPU per memorizzare il valore atomico - che non sta per essere modificato comunque da altri thread, quindi è effettivamente atomico. Il caso interessante/significativo è dove la variabile atomica può essere modificata da un altro thread - quindi è il codice che modifica il valore atomico * in memoria * che è rilevante - se lo standard C++ richiede un opcode che riesca ad incrementare atomicamente l'indirizzo di memoria ed evitare una seconda lettura per ottenere il valore di 'id' nel codice della domanda. –

+0

@TonyD, ahh vedo; grazie per il chiarimento e anche la tua risposta! Apprezzato imparare qualcosa di nuovo e vedere e comprendere l'importanza e il contesto dell'atomicità nelle letture nella memoria condivisa tra i thread. –

+0

no worries - è stato bello vedere alcuni assemblati Sparc! In realtà - il mio commento sopra parlava di richiedere un codice operativo, ma questo è fuorviante come sulle CPU che non hanno tale supporto le operazioni atomiche potrebbero ricadere su un mutex e avere molti codici macchina coinvolti. Saluti. –

7

La specifica delle funzioni di incremento atomiche dare una visione cruciale nel loro comportamento - da http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith per Integral T tipi:

T operator++(); 
T operator++() volatile; 
T operator++(int); 
T operator++(int) volatile; 

Avviso ritornano un T per valore, e non tornare il solito T& da un pre -incremento. Per questo motivo, la "lettura" del valore post-incrementato non è una seconda operazione distinta e fa parte dell'operazione di incremento atomico stessa.

Vedere anche il testo "Valore restituito" e "Nota" nella pagina in alto.

+1

grazie !!!, usando lo studio visivo 2013 per eseguire il debug del codice, si chiama atomic_fetch_add per mantenere atomico. –

+0

ottimo riferimento, grazie! –

2
static std::atomic<int> i; // global variable 
// in the thread 
int id = ++i; 

quando utilizza l'assegnazione in diversi thread, è il valore unico id?

Sì. Le variabili atomiche C++ assicurano che il valore ++i venga valutato in modo atomico, pertanto ciascun valore di id su thread diversi è univoco.

L'espressione id = ++i; viene eseguita seguendo i passaggi.

  1. atomicamente INCREMENT i, e sub-espressione (++i) viene valutato valore post-incremento.
  2. assegnare "valore valutato" a id. (questo passaggio non è atomico)
Problemi correlati