Ce face sync.Map.CompareAndSwap
CompareAndSwap(k, vechi, nou) pune nou doar dacă valoarea curentă a cheii este egală cu vechi, și spune dacă a reușit. Așa eviți să suprascrii o modificare făcută între timp de alt goroutine: citești, calculezi, apoi scrii doar dacă nimeni nu a schimbat valoarea.
CompareAndDelete(k, vechi) șterge cheia doar dacă are valoarea vechi. Dacă cheia lipsește, ambele returnează false. Valoarea vechi trebuie să fie de un tip comparabil (numere, texte, pointeri), altfel apare panică.
Ce returnează
true dacă schimbarea sau ștergerea s-a făcut.
Exemple
Exemplul 1: Schimbarea stării unei comenzi
package main
import (
"fmt"
"sync"
)
func main() {
var comenzi sync.Map
comenzi.Store(101, "nouă")
fmt.Println("nouă -> plătită:", comenzi.CompareAndSwap(101, "nouă", "plătită"))
fmt.Println("nouă -> anulată:", comenzi.CompareAndSwap(101, "nouă", "anulată"))
v, _ := comenzi.Load(101)
fmt.Println("Stare:", v)
fmt.Println("Șterg dacă e anulată:", comenzi.CompareAndDelete(101, "anulată"))
fmt.Println("Șterg dacă e plătită:", comenzi.CompareAndDelete(101, "plătită"))
_, exista := comenzi.Load(101)
fmt.Println("Mai există:", exista)
}
Rezultat
nouă -> plătită: true
nouă -> anulată: false
Stare: plătită
Șterg dacă e anulată: false
Șterg dacă e plătită: true
Mai există: false
Exemplul 2: Contor de voturi cu reîncercare
Fiecare goroutine citește valoarea, o mărește și încearcă să o scrie. Dacă alt goroutine a fost mai rapid, CompareAndSwap eșuează și bucla încearcă din nou.
package main
import (
"fmt"
"sync"
)
func voteaza(m *sync.Map, candidat string) {
for {
v, _ := m.LoadOrStore(candidat, 0)
if m.CompareAndSwap(candidat, v, v.(int)+1) {
return
}
}
}
func main() {
var voturi sync.Map
var wg sync.WaitGroup
for i := range 300 {
wg.Go(func() {
if i%3 == 0 {
voteaza(&voturi, "Ion")
} else {
voteaza(&voturi, "Maria")
}
})
}
wg.Wait()
ion, _ := voturi.Load("Ion")
maria, _ := voturi.Load("Maria")
fmt.Println("Ion:", ion, "Maria:", maria)
}
Rezultat
Ion: 100 Maria: 200
Greșeli frecvente
- Folosești
CompareAndSwapcu valori slice sau map: nu sunt comparabile și programul se oprește cu panică. - Nu verifici rezultatul
bool. Dacă schimbarea nu s-a făcut, modificarea ta s-a pierdut; reîncearcă sau tratează cazul.
Comenzi înrudite
sync.Map: Un map sigur pentru goroutine fără mutex extern: Store, Load, Delete, Range și Clear pot fi apelate concurent.sync.Map.LoadOrStore: Operații combinate fără cursă de date: LoadOrStore pune doar dacă lipsește, LoadAndDelete scoate, Swap înlocuiește.sync/atomic.CompareAndSwapInt64: CompareAndSwapInt64 scrie doar dacă valoarea e cea așteptată; SwapInt64 scrie și returnează valoarea veche.