Sobes.tech
Middle

Na co si dát pozor při práci s mutexy v Go?

sobes.tech AI

Odpověď od AI

Při práci s mutexy (sync.Mutex) v Go je důležité mít na paměti následující:

  • Zámek a odemknutí: Mutex je nutné před přístupem ke sdíleným datům uzamknout (Lock()) a po dokončení práce odemknout (Unlock()). Použití defer mu.Unlock() ihned po mu.Lock() je dobrá praxe, která pomáhá zabránit zapomenutí odemknutí.
  • Grant synchronizace: Mutex poskytuje výhradní přístup k prostředku jednomu gorutině současně.
  • Stav "zamčeno/odemčeno": Opakované volání Lock() na již zamčeném mutexu způsobí zablokování volající gorutiny, dokud jiná gorutina neuvolní mutex. Volání Unlock() na odemčeném mutexu způsobí paniku během běhu.
  • Nekopírovat mutexy: Struktura sync.Mutex není určena k kopírování po prvním použití. Předání mutexu hodnotou do funkce nebo kopírování struktury obsahující mutex může vést k nesprávné synchronizaci a panice.
  • Deadlock (zaseknutí): Vzniká, když dvě nebo více gorutin čekají na sebe, aby uvolnily prostředky. Typický scénář: gorutina A zamkne R1 a čeká na R2, zatímco gorutina B zamkne R2 a čeká na R1.
  • Livelock (živé zamčení): Situace, kdy gorutiny neustále mění svůj stav v reakci na akce ostatních, ale nedělají užitečnou práci a neposunou se dopředu. To je vzácnější než deadlock.
  • Hladem (starvation): Situace, kdy jedna nebo více gorutin nemůže získat přístup ke sdílenému prostředku kvůli jeho neustálému zámku jinými gorutinami. V Go mutexech není hladem garantován.
  • Použití sync.RWMutex pro čtení/zápis: Pokud většina operací je čtení a zápis je vzácný, může být efektivnější sync.RWMutex. Umožňuje více gorutinám číst současně, ale při zápisu blokuje jak čtení, tak i jiné zápisy.
  • Krátké zámky: Snažte se držet zámky co nejkratší dobu. Čím déle je mutex zamčený, tím větší je pravděpodobnost, že ostatní gorutiny budou čekat, což snižuje paralelismus.
  • Testování na závody: Použití vlajky -race při sestavování a spouštění testů (go test -race) pomáhá odhalit závody, které jsou často způsobeny nesprávným použitím mutexů.

Příklad použití defer:

import (
	"sync"
)

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

// Inc bezpečně zvyšuje čítač pro daný klíč.
func (c *SafeCounter) Inc(key string) {
	c.mu.Lock()
	// Odkládáme odemknutí na konec funkce.
	defer c.mu.Unlock()
	c.v[key]++
}

// Value vrací aktuální hodnotu čítače pro daný klíč.
func (c *SafeCounter) Value(key string) int {
	c.mu.Lock()
	// Odkládáme odemknutí po získání hodnoty.
	defer c.mu.Unlock()
	return c.v[key]
}