Ce face sync/atomic.Int64
atomic.Int64 este o structură care ține un int64 și permite doar acces atomic la el. Valoarea zero înseamnă 0, deci nu trebuie inițializat. n.Add(d) adună și returnează valoarea nouă, n.Load() citește, n.Store(v) scrie, iar n.Swap(v) scrie și returnează valoarea veche.
Față de funcțiile atomic.AddInt64 și celelalte, tipul are două avantaje: nu poți citi sau scrie valoarea direct din greșeală și este aliniat corect în memorie pe orice sistem. Se pune ușor într-o structură, ca în al doilea exemplu.
Ca și mutexul, nu trebuie copiat după prima folosire: structurile care îl conțin se transmit prin pointer.
Ce returnează
Add: valoarea nouă. Load: valoarea curentă. Swap: valoarea veche. Store: nimic.
Exemple
Exemplul 1: Toate cele patru metode
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var bilete atomic.Int64
bilete.Store(500)
var wg sync.WaitGroup
for range 120 {
wg.Go(func() {
bilete.Add(-1) // un bilet vândut
})
}
wg.Wait()
fmt.Println("Bilete rămase:", bilete.Load())
fmt.Println("Adaug 20:", bilete.Add(20))
vechi := bilete.Swap(1000)
fmt.Println("Swap: vechi =", vechi, "nou =", bilete.Load())
}
Rezultat
Bilete rămase: 380
Adaug 20: 400
Swap: vechi = 400 nou = 1000
Exemplul 2: Statistici într-o structură
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type Statistici struct {
cereri atomic.Int64
erori atomic.Int64
octeti atomic.Int64
}
func (s *Statistici) Inregistreaza(marime int64, ok bool) {
s.cereri.Add(1)
s.octeti.Add(marime)
if !ok {
s.erori.Add(1)
}
}
func main() {
var st Statistici
var wg sync.WaitGroup
for i := range 100 {
wg.Go(func() {
st.Inregistreaza(int64(1000+i), i%25 != 0)
})
}
wg.Wait()
fmt.Println("Cereri:", st.cereri.Load())
fmt.Println("Erori: ", st.erori.Load())
fmt.Println("Octeți:", st.octeti.Load())
}
Rezultat
Cereri: 100
Erori: 4
Octeți: 104950
Greșeli frecvente
- Copiezi structura care conține
atomic.Int64(receptor valoare sau atribuire).go vetavertizează; copia are propria valoare și modificările se pierd. - Faci
if n.Load() > 0 { n.Add(-1) }ca să nu scazi sub zero. Între cele două apeluri alt goroutine poate scădea; folosește o buclă cuCompareAndSwap.
Comenzi înrudite
sync/atomic.Int64.CompareAndSwap: Scrie valoarea nouă doar dacă valoarea curentă este cea așteptată; baza operațiilor atomice proprii, cu reîncercare.sync/atomic.Int64.And: Aplică atomic un ȘI (And) sau un SAU (Or) pe biți; folosit pentru steaguri (flags) partajate între goroutine.sync/atomic.Int32: Un int32 cu operații atomice (Add, Load, Store, Swap, CompareAndSwap); la fel ca atomic.Int64, dar pe 32 de biți.sync/atomic.AddInt64: Adună (AddInt64), citește (LoadInt64) și scrie (StoreInt64) un int64 atomic, sigur între goroutine, fără mutex.sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().