C ++ std atomic

C ++ std atomic

Usiamo "Std Atomic" se vogliamo conservare l'atomicità dell'operazione in C++. L'atomicità è il concetto che possiamo affrontare quando stiamo lavorando in operazioni/applicazioni multithreading in cui anche le semplici funzioni come la lettura e la scrittura nell'esecuzione contemporaneamente possono creare problemi o comportamenti non definiti nel codice. Per affrontare una situazione del genere, C ++ ha definito la libreria "Std :: Atomic" che garantisce una coerenza che è abbastanza sequenziale per l'esecuzione della lettura e della scrittura per vari oggetti distinti che raffigurano il comportamento ben definito. Se un thread sta scrivendo in qualche momento, l'altro thread sta leggendo in quel momento.

Procedura:

Questo articolo esplorerà cos'è l'atomicità e come possiamo usare i concetti di std atomic per affrontare i comportamenti indefiniti nei nostri codici. Discuteremo le varie funzioni di MST Atomic e implementeremo i vari esempi di Std Atomic. La funzione sotto la MST Atomic che implementeremo nei diversi esempi è data come segue:

  • Lettura e scrittura più semplici di valori
  • Rilasciare e acquisire ordini con memoria (modello)
  • Modello di scambio
  • Operazione di recupero

Lettura e scrittura più semplici di valori

Creiamo un'applicazione multithread in questo esempio in cui creiamo due thread: un thread per leggere i valori e l'altro thread per scrivere i valori. Con l'aiuto di questo esempio, cercheremo di ottenere il concetto di atomica STD, quali sono i comportamenti indefiniti durante l'esecuzione delle applicazioni multithread e il modo in cui l'atomico STD elimina i comportamenti indefiniti.

Per questo, iniziamo semplicemente il codice assegnando due valori diversi a due diverse variabili di tipo intero. Innanzitutto, inizializziamo la variabile "A" e "B" con i tipi di dati interi. Quindi, creiamo la funzione scritta nel vuoto. In questa funzione, assegniamo i valori sia a "A" che a "B", E.G. 25 e 20, rispettivamente.

Quindi, creiamo la funzione di lettura. Nella funzione di lettura, i valori di "A" e "B" vengono letti con "std :: cout <

Produzione:

L'output di questo esempio mostra il comportamento non definito dell'applicazione poiché l'output del codice è 0 o 10. Ciò è accaduto perché i thread stavano eseguendo contemporaneamente e il comando di lettura avrebbe potuto essere fatto durante l'esecuzione del comando di scrittura. In questo modo, abbiamo ottenuto un risultato incompleto nell'output.

L'Atomico STD può risolvere questo problema e può rendere i comportamenti indefiniti nell'applicazione ben definiti. Per implementare questo, apportiamo semplicemente una piccola modifica durante l'inizializzazione e l'impostazione dei valori e dei tipi di dati delle variabili definite usando "std :: atomic". Definiamo la variabile “A” e “B” come variabili atomiche di "Std :: Nome della variabile atomica". Facciamo anche una piccola modifica nella funzione di scrittura in cui, in precedenza, abbiamo semplicemente assegnato i valori a A e B utilizzando l'operatore di assegnazione "=". Ma qui, assegniamo i valori usando il "nome variabile. Metodo Store (Value) ". Usiamo il "nome variabile. load () "nella funzione di lettura. Il resto è lo stesso dell'esempio precedente.

I valori nell'output vengono letti e scritti in modo ben definito. E il multithreading è anche supportato qui.

Rilasciare e acquisire ordini con memoria (modello)

Il modello di memoria può avere un impatto enorme sulle funzioni di lettura e scrittura dell'Atomico. Il modello di memoria è una funzione predefinita che si assicura della coerenza dell'ordinamento sequenziale. Uno dei modelli atomici più interessanti è il modello di rilascio e acquisizione in cui possiamo archiviare il rilascio dell'ordine di memoria per il primo thread e l'ordine di memoria acquisito per il secondo thread, il che significa che qualsiasi negozio/scrittura atomico o non atomico è fatto Prima nel primo thread prima del secondo thread, io.e. carico.

Quindi, nell'esempio, possiamo persino cambiare la variabile atomica "A" in non atomica e la seconda variabile "B" è mantenuta atomica. Nella funzione di scrittura, archiviamo la variabile non atomica "A" semplicemente assegnandolo qualsiasi valore, e.G. 30. E memorizziamo correttamente il valore per la variabile atomica "b" usando "b. Store (valore, std :: memory_order_release) ". Lo stesso è fatto anche nella funzione di lettura in cui utilizziamo "std :: cout <

Produzione:

L'atomicità dell'operazione è ancora mantenuta con il rilascio e il modello di memoria acquisito anche quando avevamo una variabile X non atomica. Questo è successo a causa degli Y (atomico.memorizzare) che ha assicurato la manutenzione della coerenza sequenziale.

Modello di scambio

Exchange significa quando scambiamo il valore di una variabile (atomica) a un altro valore. In cambio, il valore viene prima scambiato e quindi viene restituito il valore precedente che viene scambiato da quello nuovo. Una volta scambiato il valore, si riflette su ogni operazione successiva a tale valore. Implettiamo questo scambio di variabili atomiche con l'aiuto di un esempio.

In questo esempio, introduciamo innanzitutto il foobar variabile atomico globale che ha un valore pari a "15". Nella principale, facciamo un thread come thread1 e gli assegniamo un valore intero uguale a 2. Quindi, nel ciclo per, impostiamo l'indice da 0 a 100 volte. Quindi, sostituiamo il valore della variabile Foobar a 2 usando "Foobar. Exchange (valore) ". Dopodiché, usciamo dal ciclo e cariciamo il valore della variabile Foobar per stamparlo. Dopo aver caricato il valore di Foobar, ora scambiamo il suo valore con 18 con ".Exchange (valore da sostituire con) "Metodo. E poi di nuovo, caricare i valori di Foobar e visualizzarli usando il metodo di stampa.

Qui in questo esempio, il thread deve scambiare i valori per cento volte e il valore di Foobar viene scambiato da 15 a 28. Qualsiasi operazione dopo questo scambio restituisce lo stesso valore che può essere visto nell'output.

Andare a prendere

Fetch è uguale alla funzione di scambio che scrive i valori e restituisce i valori precedentemente recuperati. Questa operazione prende il valore che viene archiviato prima che venga applicata qualsiasi operazione. Ora implementiamo l'aggiunta di recupero e il recupero sottrai in questo esempio. Definiamo una variabile atomica con il tipo di dati non firmato come "conta" e inizializza il conteggio con zero. Quindi, creiamo due funzioni: una per l'aggiunta di recupero e un'altra per sottrarre. Eseguiamo il contatore dell'incremento 1 per l'aggiunta e il decremento 1 per sottrarre in entrambe queste funzioni. Quindi, stampiamo questi valori da entrambe le funzioni Fetch_add e fetch_sub nel principale.

La funzione Fetch_add ha restituito 0 e 1 come valori precedenti prima dell'incremento. Allo stesso modo, il fetch_sub ha restituito 2 e 1 come valori precedentemente memorizzati prima della sottrazione o del decremento di uno.

Conclusione

Abbiamo implementato le operazioni di base in "Std :: Atomic" in questo articolo. Abbiamo imparato come possiamo affrontare i problemi nelle applicazioni multithreading utilizzando l'STD Atomic. Abbiamo implementato i vari esempi in C ++ per le diverse funzioni come Fetch, Exchange, Lettura/Scrittura e Modello di memoria dell'Atomico STD per garantire la coerenza sequenziale e i comportamenti ben definiti del codice per applicazioni multithread.