Middle
Na čo si treba dávať pozor pri práci s mutexmi v Go?
sobes.tech AI
Odpoveď od AI
Pri práci s mutexmi (sync.Mutex) v Go je dôležité mať na pamäti nasledujúce:
- Zámok a odomknutie: Mutex je nutné pred prístupom ku zdieľaným dátam uzamknúť (
Lock()) a po dokončení práce ich odomknúť (Unlock()). Použitiedefer mu.Unlock()ihneď pomu.Lock()je dobrá prax na zabránenie zabudnutému odomknutiu. - Grant synchronizácie: Mutex poskytuje výhradný prístup k zdroju jednému gorutinu súčasne.
- Stav "zamknuté/odomknuté": Opakované volanie
Lock()na už zamknutom mutexe spôsobí zablokovanie volajúcej gorutiny, kým iná gorutina neuvoľní mutex. VolanieUnlock()na odomknutom mutexe spôsobí pániku počas behu. - Nekopírujte mutexy: Štruktúra
sync.Mutexnie je určená na kopírovanie po prvom použití. Prenos mutexu hodnotou do funkcie alebo kopírovanie štruktúry obsahujúcej mutex môže viesť k nesprávnej synchronizácii a pánike. - Deadlock (zaseknutie): Vzniká, keď dve alebo viac gorutín čakajú na seba, aby uvoľnili zdroje. Typický scenár: gorutina A zablokuje zdroj R1 a čaká na R2, zatiaľ čo gorutina B zablokuje R2 a čaká na R1.
- Livelock (živé zablokovanie): Situácia, keď gorutiny neustále menia svoj stav v reakcii na akcie ostatných, ale nerobia užitočnú prácu a nepostupujú dopredu. Toto je zriedkavejšie ako deadlock.
- Hladovanie (starvation): Situácia, keď jedna alebo viac gorutín nemôže získať prístup ku zdieľanému zdroju kvôli jeho neustálemu zámku inými gorutinami. V Go mutexoch nie je hladovanie garantované.
- Použitie
sync.RWMutexpre čítanie/zápis: Ak väčšina operácií je čítanie a zápis je zriedkavý, môže byť efektívnejšísync.RWMutex. Umožňuje viacerým gorutinám čítať súčasne, ale pri zápise blokuje ako čítanie, tak aj iné zápisy. - Krátke zámky: Snažte sa držať zámky čo najkratšie. Čím dlhšie je mutex zamknutý, tým väčšia je pravdepodobnosť, že ostatné gorutiny budú čakať, čo znižuje paralelizmus.
- Testovanie na závody: Použitie vlajky
-racepri kompilácii a spustení testov (go test -race) pomáha odhaliť závody, často spôsobené nesprávnym použitím mutexov.
Príklad použitia defer:
import (
"sync"
)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
// Inc bezpečne zvyšuje čítač pre daný kľúč.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Odkladáme uvoľnenie na koniec funkcie.
defer c.mu.Unlock()
c.v[key]++
}
// Value vracia aktuálnu hodnotu čítača pre daný kľúč.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Odkladáme uvoľnenie po získaní hodnoty.
defer c.mu.Unlock()
return c.v[key]
}