Sobes.tech
Middle

Jak uniknąć sytuacji zakleszczenia (deadlock) w programie Go?

sobes.tech AI

Odpowiedź od AI

Wzajemne blokowanie w Go najczęściej występuje przy nieprawidłowym użyciu mutexów i kanałów. Można go uniknąć na następujące sposoby:

  • Posortowane przejęcie mutexów: Przy przejmowaniu wielu mutexów zawsze rób to w tej samej kolejności we wszystkich gorutynach.
  • Timeouty dla blokad: Używaj kontekstów z timeoutami podczas oczekiwania na blokady lub odbioru z kanałów, aby uniknąć nieskończonego oczekiwania.
  • Kanały bez bufora: Rozumiej, że kanały bez bufora wymagają zarówno nadawcy, jak i odbiorcy do kontynuowania działania. Nieprawidłowe użycie może prowadzić do blokady.
  • Kanały z buforem: Właściwie wybierz rozmiar bufora kanału. Zbyt mały bufor może prowadzić do blokady, jeśli nadawcy i odbiorcy nie są zsynchronizowani.
  • Użycie select: Przy pracy z wieloma kanałami używaj operatora select z gałęzią default lub timeoutem, aby nie blokować się na oczekiwaniu na jeden kanał.
  • Analiza kodu: Używaj statycznej analizy (go vet, staticcheck) i narzędzi do wykrywania warunków wyścigu (go run -race), które mogą również pomóc zidentyfikować potencjalne miejsca występowania wzajemnych blokad.
  • Hierarchia zasobów: Jeśli zasoby mają hierarchię, przejmuj je od góry do dołu.
  • Unikaj cyklicznych zależności: Nie twórz sytuacji, w których gorutyna A czeka na zasób zajęty przez B, a B czeka na zasób zajęty przez A.

Przykład użycia select z timeoutem:

import (
	"context"
	"time"
)

func przykład(ch chan int) {
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	select {
	case val := <-ch:
		// Obsługa wartości
		_ = val
	case <-ctx.Done():
		// Upłynął timeout
	}
}