Ce face std::atomic
Operațiile pe un std::atomic<T> sunt indivizibile: niciun alt fir nu poate vedea o valoare „la jumătate”, iar ++x nu poate pierde incrementări, chiar dacă mai multe fire îl apelează simultan. Pentru contoare și steaguri simple este mai rapid decât un mutex.
Funcționează cu tipuri simple: int, bool, pointeri etc. (std::atomic_int, std::atomic_bool sunt alias-uri). Operații: load, store, exchange, fetch_add/fetch_sub (returnează valoarea veche), ++, += și compare_exchange_strong (schimbă valoarea doar dacă este cea așteptată).
Un singur atomic este sigur, dar o operație formată din mai mulți pași (citește, decide, scrie) nu devine automat atomică; pentru asta ai nevoie de compare_exchange sau de un mutex.
Ce returnează
load returnează valoarea; fetch_add returnează valoarea dinainte; compare_exchange_strong returnează true dacă a făcut schimbul.
Exemple
Exemplul 1: Un contor fără mutex
#include <atomic>
#include <print>
#include <thread>
#include <vector>
int main() {
std::atomic<int> descarcari{0};
std::vector<std::thread> fire;
for (int i = 0; i < 8; ++i) {
fire.emplace_back([&descarcari] {
for (int j = 0; j < 5'000; ++j) ++descarcari;
});
}
for (auto& t : fire) t.join();
std::println("Descărcări: {}", descarcari.load());
}
Rezultat
Descărcări: 40000
Exemplul 2: fetch_add și compare_exchange_strong
compare_exchange_strong schimbă valoarea doar dacă este cea așteptată; altfel pune în asteptat valoarea reală.
#include <atomic>
#include <print>
int main() {
std::atomic<int> locuri{10};
int inainte = locuri.fetch_add(5);
std::println("înainte: {}, acum: {}", inainte, locuri.load());
int asteptat = 15;
bool ok = locuri.compare_exchange_strong(asteptat, 0);
std::println("schimbat: {}, acum: {}", ok, locuri.load());
asteptat = 15;
ok = locuri.compare_exchange_strong(asteptat, 99);
std::println("schimbat: {}, valoarea reală era: {}", ok, asteptat);
}
Rezultat
înainte: 10, acum: 15
schimbat: true, acum: 0
schimbat: false, valoarea reală era: 0
Greșeli frecvente
- Folosești un
intobișnuit sau unboolmodificat de mai multe fire, „pentru că e doar un număr”. Este cursă de date, deci comportament nedefinit; foloseștestd::atomic. - Scrii
if (x.load() > 0) x--;crezând că este sigur. Între citire și scădere alt fir poate modifica valoarea; folosește o buclă cucompare_exchangesau un mutex.
Comenzi înrudite
std::mutex: Încuietoare pentru date comune: un singur fir de execuție o poate ține la un moment dat.std::thread: Pornește o funcție pe un fir de execuție separat, care rulează în paralel cu restul programului.std::jthread: Fir de execuție care face join automat la distrugere și poate fi rugat să se oprească (stop_token).