Sobes.tech
Middle

Ką reikia prisiminti dirbant su mutex'ais Go?

sobes.tech AI

Atsakymas iš AI

Dirbant su mutex (sync.Mutex) Go kalboje svarbu atsiminti šiuos dalykus:

  • Užrakinimas ir atlaisvinimas: Mutex būtina užrakinti (Lock()) prieš prieigą prie bendro naudojimo duomenų ir atlaisvinti (Unlock()) po darbo su jais pabaigos. Naudojimas defer mu.Unlock() iškart po mu.Lock() yra gera praktika, padedanti išvengti užmirštų atlaisvinimų.
  • Sinchronizacijos garantija: Mutex suteikia išskirtinę prieigą prie ištekliaus vienu metu tik vienai gorutinai.
  • "Užrakinta/atsidengusi" būklė: Pakartotinis Lock() iškvietimas jau užrakintame mutexe sukels blokuojamąją gorutiną, kol kita gorutina neišlaisvins mutexo. Unlock() iškvietimas neužrakintame mutexe sukels paniką vykdymo metu.
  • Nekopijuokite mutexų: Struktūra sync.Mutex nėra skirta kopijavimui po pirmojo naudojimo. Perdavimas mutexo reikšme į funkciją arba struktūros kopijavimas, turinčios mutexą, gali sukelti netinkamą sinchronizaciją ir paniką.
  • Deadlock (užstrigimas): Atsitinka, kai dvi ar daugiau gorutinų laukia viena kitos, kad atlaisvintų išteklius. Tipinis scenarijus: gorutina A užrakina R1 išteklių ir laukia R2, o gorutina B užrakina R2 ir laukia R1.
  • Livelock (gyvas užraktas): Situacija, kai gorutinos nuolat keičia savo būseną atsakydamos viena į kitą, bet nedaro naudingo darbo ir nepaeina į priekį. Tai retesnė nei deadlock.
  • Badavimas: Situacija, kai viena ar kelios gorutinos negali pasiekti bendro ištekliaus dėl nuolatinio kitų gorutinų jo užėmimo. Go mutexuose badavimas nėra garantuotas.
  • Naudojant sync.RWMutex skaitymui/rašymui: Jei dauguma operacijų yra skaitymas, o rašymas retas, sync.RWMutex gali būti efektyvesnis. Jis leidžia kelioms gorutinoms skaityti vienu metu, bet rašymo metu blokuoja ir skaitymą, ir kitus rašymus.
  • Trumpi užraktai: Stenkitės laikyti užraktus kuo trumpiau. Kuo ilgiau mutexas užrakintas, tuo didesnė tikimybė, kad kitos gorutinos lauks, kas mažina paralelizmą.
  • Testavimas dėl varžybų: Naudojant -race žymeklį go test kūrimo ir paleidimo metu, galima aptikti varžybas, dažnai sukeltas netinkamo mutexų naudojimo.

Pavyzdys naudojant defer:

import (
	"sync"
)

type SafeCounter struct {
	mu sync.Mutex
	v  map[string]int
}

// Inc saugiai padidina skaitiklį nurodytu raktu.
func (c *SafeCounter) Inc(key string) {
	c.mu.Lock()
	// Atidėk atlaisvinimą iki funkcijos pabaigos.
	defer c.mu.Unlock()
	c.v[key]++
}

// Value grąžina dabartinę skaitiklio reikšmę nurodytu raktu.
func (c *SafeCounter) Value(key string) int {
	c.mu.Lock()
	// Atidėk atlaisvinimą po reikšmės gavimo.
	defer c.mu.Unlock()
	return c.v[key]
}