Devo usare atomico per la variabile bool “exit”?

Ho bisogno di impostare un flag per un altro thread per uscire. Quell’altro thread controlla di volta in volta il flag di uscita. Devo usare atomico per la bandiera o solo un semplice bool è sufficiente e perché (con un esempio di cosa esattamente potrebbe andare storto se uso il bool semplice)?

#include  bool exit = false; void thread_fn() { while(!exit) { //do stuff if(exit) break; //do stuff } } int main() { auto f = std::async(std::launch::async, thread_fn); //do stuff exit = true; f.get(); } 

Devo usare atomic per la variabile bool “exit”?

O usare atomic , o usare la sincronizzazione manuale attraverso (per esempio) uno std::mutex . Il tuo programma contiene attualmente una corsa di dati , con una discussione che potenzialmente può leggere una variabile mentre un altro thread la sta scrivendo. Questo è un comportamento indefinito.

Per il paragrafo 1.10 / 21 della norma C ++ 11:

L’esecuzione di un programma contiene una corsa di dati se contiene due azioni in conflitto in thread diversi, almeno uno dei quali non è atomico, e nessuno dei due avviene prima dell’altro. Qualsiasi razza di dati di questo genere ha un comportamento indefinito .

La definizione di ” conflitto ” è riportata nel Paragrafo 1.10 / 4:

Due valutazioni di espressione sono in conflitto se una di esse modifica una posizione di memoria (1.7) e l’altra accede o modifica la stessa posizione di memoria.

Sì, devi avere un po ‘di sincronizzazione. Il modo più semplice è, come dici tu, con atomic .

Formalmente, come dice @AndyProwl, la definizione del linguaggio dice che non usare un atomico qui dà un comportamento indefinito. Ci sono buone ragioni per questo.

Innanzitutto, una lettura o scrittura di una variabile può essere interrotta a metà tramite un interruttore del filo; l’altro thread può vedere un valore parzialmente scritto, o se modifica il valore, il thread originale vedrà un valore misto. In secondo luogo, quando due thread vengono eseguiti su core diversi, hanno cache separate; scrivendo un valore lo memorizza nella cache, ma non aggiorna altre cache, quindi un thread potrebbe non vedere un valore scritto da un altro thread. Terzo, il compilatore può riorganizzare il codice in base a ciò che vede; nel codice di esempio, se nulla all’interno del ciclo modifica il valore di exit , il compilatore non ha motivo di sospettare che il valore cambierà; può girare il ciclo dentro while(1) .

Atomics affronta tutti e tre questi problemi.

in realtà, non c’è niente di sbagliato in questo esempio particolare. l’unica nota è dichiarare la variabile di uscita bool come volatile per mantenerla in memoria. entrambe le architetture CISC e RISC implementano la lettura / scrittura bool come istruzione del processore rigorosamente atomico. anche i moderni processori multocore hanno implementato un’implementazione intelligente della cache. quindi, qualsiasi barriera di memoria non è necessaria. la citazione standard non è appropriata per questo caso particolare perché riguarda l’unica scrittura e la lettura dall’unico thread.