è l’operatore di assegnazione ‘=’ atomico?

Sto implementando la comunicazione Inter-Thread usando la variabile globale.

//global var volatile bool is_true = true; //thread 1 void thread_1() { while(1){ int rint = rand() % 10; if(is_true) { cout << "thread_1: "<< rint <<endl; //thread_1 prints some stuff if(rint == 3) is_true = false; //here, tells thread_2 to start printing stuff } } } //thread 2 void thread_2() { while(1){ int rint = rand() % 10; if(! is_true) { //if is_true == false cout << "thread_1: "<< rint <<endl; //thread_2 prints some stuff if(rint == 7) //7 is_true = true; //here, tells thread_1 to start printing stuff } } } int main() { HANDLE t1 = CreateThread(0,0, thread_1, 0,0,0); HANDLE t2 = CreateThread(0,0, thread_2, 0,0,0); Sleep(9999999); return 0; } 

Domanda

Nel codice qui sopra, utilizzo un var volatile bool is_true globale volatile bool is_true per cambiare la stampa tra thread_1 e thread_2.

Mi chiedo se è sicuro utilizzare thread-thread qui ?

Questo codice non è garantito per essere sicuro per i thread su Win32, poiché Win32 garantisce l’atomicità solo per valori allineati correttamente a 4 byte e dimensioni puntatore. bool non è garantito per essere uno di quei tipi. (In genere è un tipo da 1 byte.)

Per coloro che chiedono un esempio reale di come questo potrebbe fallire:

Supponiamo che bool sia un tipo da 1 byte. Supponiamo anche che la variabile is_true sia memorizzata adiacente ad un’altra variabile bool (chiamiamola other_bool ), in modo che entrambi condividano la stessa linea a 4 byte. Per concretezza, diciamo che is_true è all’indirizzo 0x1000 e other_bool è all’indirizzo 0x1001. Supponiamo che entrambi i valori siano inizialmente false e che un thread decida di aggiornare is_true nello stesso momento in cui un altro thread tenta di aggiornare other_bool . La seguente sequenza di operazioni può verificarsi:

  • Thread 1 si prepara a impostare is_true su true caricando il valore a 4 byte contenente is_true e other_bool . Thread 1 legge 0x00000000.
  • Thread 2 si prepara a impostare other_bool su true caricando il valore a 4 byte contenente is_true e other_bool . Thread 2 legge 0x00000000.
  • Thread 1 aggiorna il byte nel valore di 4 byte corrispondente a is_true , producendo 0x00000001.
  • Thread 2 aggiorna il byte nel valore a 4 byte corrispondente a other_bool , producendo 0x00000100.
  • La discussione 1 memorizza il valore aggiornato nella memoria. is_true è ora true e other_bool è ora false .
  • Thread 2 memorizza il valore aggiornato in memoria. is_true ora è false e other_bool è ora true .

Osserva che alla fine di questa sequenza, l’aggiornamento a is_true stato perso, perché è stato sovrascritto dal thread 2, che ha catturato un vecchio valore di is_true .

Accade così che x86 stia perdonando questo tipo di errore perché supporta gli aggiornamenti granulari e ha un modello di memoria molto stretto. Altri processori Win32 non sono così indulgenti. I chip RISC, ad esempio, spesso non supportano gli aggiornamenti granulari di byte e, anche se lo fanno, di solito hanno modelli di memoria molto deboli.

no, non è ….. devi usare un primitivo di chiusura di qualche tipo. A seconda della piattaforma, è ansible utilizzare quelli di boost o, se si esegue una finestra nativa, qualcosa come InterlockedCompareExchange.

In effetti nella tua situazione potresti usare alcuni dei parametri di sicurezza dei thread in modo da poter “segnalare” l’altro thread per iniziare a fare quello che vuoi.

Su tutti i processori moderni, si può presumere che le letture e le scritture di tipi nativi allineati in modo naturale siano atomici. Finché il bus di memoria è largo almeno quanto il tipo letto o scritto, la CPU legge e scrive questi tipi in una singola transazione bus, rendendo imansible per gli altri thread vederli in uno stato incompleto. Su x86 e x64, non è garantito che le letture e le scritture più grandi di otto byte siano atomiche. Ciò significa che le letture e le scritture a 16 byte dei registri di estensione SIMD in streaming (SSE) e le operazioni con le stringhe potrebbero non essere atomiche.

Le letture e le scritture di tipi che non sono allineati naturalmente, ad esempio, la scrittura di DWORD che attraversano i limiti a quattro byte, non sono garantiti come atomici. La CPU potrebbe dover fare queste letture e scritture come transazioni multiple di bus, che potrebbero consentire ad un altro thread di modificare o vedere i dati nel mezzo della lettura o della scrittura.

La sicurezza del thread di questo pezzo di codice non dipende dall’atomicità del compito. Entrambe le routine di thread funzionano rigorosamente a turno. Non ci sono condizioni di gara: thread_1 produrrà roba fino a ottenere un certo numero casuale dopo il quale lascerà la ‘sezione di output’ e lascerà che l’altro thread funzioni in esso. Ci sono un paio di cose che vale la pena notare:

  • La funzione rand () potrebbe non essere thread-safe (non il problema nel codice qui indicato)
  • non si dovrebbe usare la funzione Win32 CreateThread (), specialmente quando si usano le funzioni libbre di CRT che (potenzialmente) utilizzano variabili globali. Usa invece _beginthreadex ().