Sobes.tech
Junior

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:

  1. 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 mu1 i czeka na mu2. Goroutine2 zablokowała mu2 i czeka na mu1. Obie gorutyny są zablokowane.

  2. 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.

  3. 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ć select z default lub timeoutami w pracy z kanałami, gdy jest to konieczne.
  • Ostrożnie zarządzać czasem życia gorutyn i zamykaniem kanałów.