Ce face sync.Map
Un map obișnuit nu poate fi scris din mai multe goroutine în același timp (programul se oprește cu fatal error: concurrent map writes). sync.Map poate: toate metodele lui sunt sigure pentru folosire concurentă. Valoarea zero este un map gol, gata de folosit.
Store(k, v) pune o valoare, Load(k) o citește (cu ok = dacă există), Delete(k) o șterge, Clear() golește tot, iar Range(f) parcurge perechile până când f returnează false. Cheile și valorile sunt de tip any, deci la citire ai nevoie de o conversie (v.(int)). Ordinea din Range nu este garantată.
Documentația recomandă sync.Map în două cazuri: chei scrise o dată și citite de multe ori (cache care doar crește) sau goroutine care lucrează pe chei diferite. În celelalte cazuri, un map obișnuit protejat de sync.Mutex are tipuri sigure și este la fel de rapid.
Ce returnează
Load: valoarea (any) și ok. Store, Delete, Range, Clear nu returnează nimic.
Exemple
Exemplul 1: Operațiile de bază
Cheile din Range sunt adunate și sortate, pentru că ordinea parcurgerii nu este fixă.
package main
import (
"fmt"
"slices"
"sync"
)
func main() {
var stoc sync.Map
stoc.Store("mere", 120)
stoc.Store("pere", 45)
stoc.Store("prune", 80)
if v, ok := stoc.Load("mere"); ok {
fmt.Println("mere:", v.(int), "kg")
}
_, ok := stoc.Load("banane")
fmt.Println("banane există:", ok)
stoc.Delete("pere")
var chei []string
stoc.Range(func(k, v any) bool {
chei = append(chei, fmt.Sprintf("%s=%d", k, v))
return true // continuă parcurgerea
})
slices.Sort(chei)
fmt.Println("Stoc:", chei)
stoc.Clear()
n := 0
stoc.Range(func(k, v any) bool { n++; return true })
fmt.Println("După Clear:", n, "produse")
}
Rezultat
mere: 120 kg
banane există: false
Stoc: [mere=120 prune=80]
După Clear: 0 produse
Exemplul 2: Scrieri din multe goroutine
Fiecare goroutine scrie cheia lui. Cu un map obișnuit fără mutex, programul s-ar putea opri cu eroare fatală.
package main
import (
"fmt"
"sync"
)
func main() {
var patrate sync.Map
var wg sync.WaitGroup
for i := 1; i <= 50; i++ {
wg.Go(func() {
patrate.Store(i, i*i)
})
}
wg.Wait()
suma := 0
patrate.Range(func(_, v any) bool {
suma += v.(int)
return true
})
v, _ := patrate.Load(7)
fmt.Println("7² =", v)
fmt.Println("Suma pătratelor 1..50 =", suma)
}
Rezultat
7² = 49
Suma pătratelor 1..50 = 42925
Greșeli frecvente
- Faci
v.(int)pe o valoare salvată caint64saustring: programul se oprește cu panică. Folosește forma sigurăn, ok := v.(int). - Faci
Loadși apoiStoreca să „adaugi doar dacă lipsește”. Între cele două apeluri alt goroutine poate scrie; foloseșteLoadOrStore. - Copiezi un
sync.Mapdupă folosire (îl transmiți prin valoare). Transmite*sync.Map.
Comenzi înrudite
sync.Map.LoadOrStore: Operații combinate fără cursă de date: LoadOrStore pune doar dacă lipsește, LoadAndDelete scoate, Swap înlocuiește.sync.Map.CompareAndSwap: Schimbă (CompareAndSwap) sau șterge (CompareAndDelete) o cheie doar dacă valoarea ei este încă cea așteptată.sync.RWMutex: Lacăt pentru citire și scriere: mai mulți cititori pot lucra deodată (RLock), dar un scriitor lucrează singur (Lock).sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().