Czym jest deadlock podczas pracy z goroutine?
sobes.tech AI
Odpowiedź od AI
Zablokowanie (deadlock) to sytuacja, gdy dwie lub więcej gorutyn blokuje się nawzajem, oczekując na siebie, i żadna z nich nie może kontynuować wykonywania. Najczęściej występuje podczas pracy z mutexami lub kanałami.
Przykłady deadlocków:
-
Blokada mutexów w różnej kolejności:
// Przykład deadlocka z mutexami var mu1 sync.Mutex var mu2 sync.Mutex func goroutine1() { mu1.Lock() // Zablokuj mu1 time.Sleep(100 * time.Millisecond) // Odczekaj trochę mu2.Lock() // Spróbuj zablokować mu2 (zajęty przez goroutine2) fmt.Println("goroutine1 uzyskał mu2") mu2.Unlock() mu1.Unlock() } func goroutine2() { mu2.Lock() // Zablokuj mu2 time.Sleep(100 * time.Millisecond) // Odczekaj trochę mu1.Lock() // Spróbuj zablokować mu1 (zajęty przez goroutine1) fmt.Println("goroutine2 uzyskał mu1") mu1.Unlock() mu2.Unlock() }Goroutine1 zablokowała
mu1i czeka namu2. Goroutine2 zablokowałamu2i czeka namu1. Obie gorutyny są zablokowane. -
Wysyłanie do kanału bez bufora bez odbiorcy:
// Przykład deadlocka z kanałem func main() { ch := make(chan int) //go func() { // Jeśli nie uruchomi się gorutyna odczytująca, wystąpi deadlock // <-ch //}() ch <- 1 // Wysyłanie do kanału bez aktywnego odbiorcy fmt.Println("Wysłano do kanału") // Ta linia nie zostanie osiągnięta }Wysyłanie do kanału bez bufora blokuje się, dopóki inna gorutyna nie zacznie czytać z niego. Jeśli nie ma odbiorcy, występuje deadlock.
-
Odczyt z kanału bez nadawcy (po zamknięciu lub z pustym buforem):
// Przykład deadlocka podczas odczytu z kanału func main() { ch := make(chan int, 1) // Kanał z buforem na 1 element ch <- 1 // Wysłano 1 close(ch) // Zamknięcie kanału <-ch // Odczyt 1 <-ch // Próba odczytu ponownie z zamkniętego i pustego kanału - deadlock }Odczyt z zamkniętego niepustego kanału zakończy się sukcesem. Odczyt z zamkniętego i pustego kanału lub z otwartego, ale pustego bez nadawcy spowoduje blokadę.
Runtime Golanga może wykryć niektóre typy deadlocków (np. gdy wszystkie zablokowane gorutyny oczekują na odbiór lub wysłanie do pojedynczej gorutyny), a w takim przypadku program zostanie nagle zakończony komunikatem "fatal error: all goroutines are asleep - deadlock!". Jednak nie wszystkie deadlocki mogą być automatycznie wykryte.
Aby zapobiec deadlockom:
- Zachować spójny porządek przejęcia mutexów.
- Upewnić się, że dla każdego wysłania do kanału bez bufora istnieje odpowiadające odczytanie, i odwrotnie.
- Używać
selectzdefaultlub timeoutami w pracy z kanałami, gdy jest to konieczne. - Ostrożnie zarządzać czasem życia gorutyn i zamykaniem kanałów.