Ce face sync.Pool
sync.Pool păstrează obiecte care nu mai sunt folosite, ca să poată fi refolosite în loc să fie create din nou. p.Get() returnează un obiect din rezervă; dacă rezerva este goală, apelează funcția New. După ce termini, p.Put(x) pune obiectul înapoi. Este sigur pentru folosire din multe goroutine.
Este util pentru obiecte create foarte des și folosite scurt timp, de exemplu buffere (bytes.Buffer) într-un server care răspunde la mii de cereri: mai puține alocări înseamnă mai puțină muncă pentru colectorul de memorie (GC).
Rezerva nu este un cache: obiectele din ea pot fi eliberate oricând de GC, fără să fii anunțat. Nu păstra în ea date care trebuie să supraviețuiască, nu te baza pe faptul că Get returnează un obiect pus anterior și curăță obiectul (de exemplu buf.Reset()) înainte de refolosire.
Ce returnează
Get: un obiect (any), pe care îl convertești la tipul tău. Put nu returnează nimic.
Exemple
Exemplul 1: Buffere refolosite pentru mesaje
package main
import (
"bytes"
"fmt"
"sync"
)
var buffere = sync.Pool{
New: func() any { return new(bytes.Buffer) },
}
func mesaj(nume, oras string) string {
buf := buffere.Get().(*bytes.Buffer)
buf.Reset() // poate conține date de la folosirea anterioară
defer buffere.Put(buf)
buf.WriteString("Bună, ")
buf.WriteString(nume)
buf.WriteString(" din ")
buf.WriteString(oras)
buf.WriteString("!")
return buf.String()
}
func main() {
fmt.Println(mesaj("Ana", "Iași"))
fmt.Println(mesaj("Radu", "Cluj"))
fmt.Println(mesaj("Ioana", "Timișoara"))
}
Rezultat
Bună, Ana din Iași!
Bună, Radu din Cluj!
Bună, Ioana din Timișoara!
Exemplul 2: Folosire din mai multe goroutine
Fiecare goroutine ia un slice din rezervă, îl folosește și îl pune înapoi. Rezultatele sunt adunate pe poziții, apoi afișate.
package main
import (
"fmt"
"strconv"
"sync"
)
var rezerva = sync.Pool{
New: func() any {
s := make([]byte, 0, 64)
return &s
},
}
func main() {
rezultate := make([]string, 5)
var wg sync.WaitGroup
for i := range 5 {
wg.Go(func() {
p := rezerva.Get().(*[]byte)
b := (*p)[:0]
b = append(b, "factura-"...)
b = strconv.AppendInt(b, int64(2026000+i), 10)
rezultate[i] = string(b)
*p = b
rezerva.Put(p)
})
}
wg.Wait()
for _, r := range rezultate {
fmt.Println(r)
}
}
Rezultat
factura-2026000
factura-2026001
factura-2026002
factura-2026003
factura-2026004
Greșeli frecvente
- Uiți să golești obiectul după
Get: un buffer refolosit poate conține textul cererii anterioare. - Folosești
Poolca un cache de date. GC poate goli rezerva oricând; datele pierdute trebuie să poată fi recreate oricând cuNew. - Pui în rezervă slice-uri ca valori (
Put(s)): conversia laanyalocă memorie de fiecare dată. Pune un pointer (*[]byte), ca în al doilea exemplu.
Comenzi înrudite
sync.Once: Garantează că o funcție rulează o singură dată, chiar dacă Do este apelat de multe goroutine (inițializare leneșă).sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().