2014-10-30 5 views
5

Quindi sto iniziando a familiarizzare con i tipi di C++ 11 <atomic>. In passato, quando avevo una bandiera atomica, di solito chiudevo semplicemente un mutex prima di accedervi. Un'esigenza comune sarebbe quella di verificare se il flag è false e, in caso affermativo, impostarlo atomicamente su true e quindi fare qualcosa. Quindi, in pratica questo sarebbe compiuto come questo, dove flag è un semplice bool:Uso di base di condizionali con std :: atomico <T>

{ 
    std::lock_guard<std::mutex> lock(my_mutex); 
    if (!flag) 
    { 
     flag = true; 
     // do something; 
    } 
} 

Così, ora sto cercando di capire come la stessa cosa può essere realizzato con <atomic>. Il docs dice che l'operatore di assegnazione e operator T di un tipo atomico sono operazioni atomiche. Tuttavia, se cambio flag a std::atomic<bool>, immagino non posso semplice esempio:

if (!flag) 
{ 
    flag = true; 
    // do something 
} 

... perché, anche se l'espressione (!flag) è atomico, e l'assegnazione flag = true è atomico, non c'è nulla per impedire un altro thread dalla modifica del flag tra queste due istruzioni.

Quindi, se ho capito bene qui, l'unico corretto utilizzo - affatto - dei condizionali con i tipi atomici, in cui il risultato del condizionale potrebbe modificare la variabile atomica, è quello di utilizzare l'e il funzionamento Confronta Swap ? Ho ragione?

Così, ho dovuto dire:

bool expected = false; 
if (flag.compare_exchange_weak(expected, true)) 
{ 
    // do something 
} 

Ho ragione nella mia comprensione qui?

+2

['std :: atomic_flag'] (http://en.cppreference.com/w/cpp/atomic/atomic_flag) esiste esattamente per il caso d'uso che stai descrivendo, e per di più è garantito che sia lock- gratuito su tutte le piattaforme Dovresti usare il metodo 'test_and_set', che è atomico.Puoi anche usare un 'std :: atomic ' ed eseguire un 'fetch_add' su di esso, che è atomico e ti darà il valore precedente nel momento in cui viene eseguito l'incremento (questo è spesso più veloce di un CAS sulla maggior parte delle architetture, anche se immagino non più veloce di 'std :: atomic_flag', che sarebbe la mia preferenza in questo caso). – Cameron

+0

Giusto - ma sembra che non sia possibile semplicemente "controllare" atomicamente il valore di una bandiera atomica (senza impostarla) - oppure è presente? I documenti non definiscono un 'operatore bool' o qualsiasi cosa che abilita l'espressione 'if (flag)'. Mi rendo conto di non aver specificato questo requisito nella mia domanda - Mi sto solo chiedendo. A proposito di ... perché * non * c'è un modo per controllare semplicemente la bandiera atomica senza impostarla? – Siler

+0

Esatto, devi impostarlo per testarlo (che è equivalente al tuo codice di esempio come è attualmente scritto, tranne che è atomico). Se è necessario testarlo in modo indipendente, suggerisco di usare l'approccio 'fetch_add', o' compare_exchange_strong' (che è molto più chiaro, ed è [altrettanto veloce di 'fetch_add' (' lock xadd') su x86] (http: // www.agner.org/optimize/instruction_tables.pdf)). – Cameron

risposta

3

Se si dispone di più thread su cui è in esecuzione lo stesso codice che è necessario eseguire tale inversione, è necessario utilizzare compare_exchange_weak() o compare_exchange_strong() per il motivo esatto che si suggerisce (probabilmente preferibile forte).

Tuttavia, non è il caso di dire che questo è il solo uso corretto di condizionali con atomica. Se ho, diciamo, un thread che solo legge mai l'atomica e uno che scrive ad esso, è perfettamente ragionevole usare loro il modo più semplice ... per esempio:

std::atomic<bool> done{false}; 

// thread 1 
while (!done) { 
    .... 
} 

// thread 2 
stop() { done = true; } 

Non c'è alcun motivo per me di fare un done.compare_exchange_strong(expected, true) lì. Questo è eccessivo. È davvero su una base caso per caso.

+0

È chiaro dal codice OP che "compare_exchange_ {weak, strong}" è appropriato in questo caso. La domanda qualifica la parola "solo" con la condizione che il risultato del confronto sarà utilizzato per decidere se l'assegnazione si verifica. Ancora, una buona risposta completa per il più ampio insieme di casi d'uso. –

1

Sì.

Detto questo, si può preferire compare_exchange_strong per questo, a meno che non si sia in un ciclo stretto. Può portare a prestazioni ridotte, ma è garantito per darti il ​​risultato che ti aspetti (che non è lo compare_exchange_weak).

0

Nota ovvia: la semantica del blocco è molto diversa dalla semplice verifica atomica poiché in questo caso l'altro thread attende fino a quando il blocco non viene rilasciato e eseguirà sempre le azioni dopo l'acquisizione del blocco. Quindi, in senso stretto due esempi di codice non sono equivalenti.

Problemi correlati