Ce face AtomicUsize
AtomicUsize ține un usize pe care toate firele îl pot modifica prin &, fără lacăt. Operațiile atomice nu pot fi întrerupte la jumătate: fetch_add(n, ...) adună n și returnează valoarea de dinainte, load(...) citește valoarea, store(v, ...) o înlocuiește.
Argumentul Ordering spune ce garanții de ordine are operația față de restul memoriei. Dacă nu ești sigur, folosește Ordering::SeqCst, cea mai strictă. Pentru un simplu contor de statistică ajunge Ordering::Relaxed.
Există tipuri atomice și pentru alte valori: AtomicBool, AtomicI32, AtomicU64 etc., cu aceleași metode. Pentru că new este const, pot fi static.
Parametri
| Parametru | Descriere |
|---|---|
val |
Valoarea adunată (fetch_add) sau scrisă (store). |
order |
Ordinea memoriei: SeqCst, Relaxed, Acquire, Release, AcqRel. |
Ce returnează
fetch_add: valoarea de dinainte. load: valoarea curentă. store: nimic.
Exemple
Exemplul 1: Contor de cereri din mai multe fire
Patru fire adună câte 250; rezultatul final este mereu 1000. Nu e nevoie de Mutex.
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
static CERERI: AtomicUsize = AtomicUsize::new(0);
fn main() {
thread::scope(|s| {
for _ in 0..4 {
s.spawn(|| {
for _ in 0..250 {
CERERI.fetch_add(1, Ordering::Relaxed);
}
});
}
});
println!("cereri: {}", CERERI.load(Ordering::SeqCst));
}
Rezultat
cereri: 1000
Exemplul 2: fetch_add returnează valoarea veche; store o înlocuiește
Fiecare comandă primește un număr unic: valoarea returnată de fetch_add.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let urmatorul_id = AtomicUsize::new(100);
for client in ["Ana", "Mihai", "Ioana"] {
let id = urmatorul_id.fetch_add(1, Ordering::SeqCst);
println!("comanda #{id} pentru {client}");
}
println!("următorul id: {}", urmatorul_id.load(Ordering::SeqCst));
urmatorul_id.store(1, Ordering::SeqCst);
println!("după resetare: {}", urmatorul_id.load(Ordering::SeqCst));
}
Rezultat
comanda #100 pentru Ana
comanda #101 pentru Mihai
comanda #102 pentru Ioana
următorul id: 103
după resetare: 1
Greșeli frecvente
- Faci
let v = a.load(..); a.store(v + 1, ..);din mai multe fire: între cele două operații alt fir poate scrie și se pierd adunări. Foloseștefetch_add. - Te aștepți ca
fetch_addsă returneze valoarea nouă: returnează valoarea de dinainte de adunare. - Depășirea nu este verificată:
fetch_addtrece prin 0 la depășire (wrapping), fără panică.
Comenzi înrudite
Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.thread::scope: Pornește fire care pot împrumuta variabile locale; scope așteaptă automat terminarea tuturor firelor create în el.Arc: Varianta lui Rc care poate fi împărțită între fire: contorul de proprietari este actualizat atomic.Cell: Celulă cu mutabilitate interioară pentru valori Copy: get() copiază valoarea afară, set() pune una nouă, prin &self.