Sobes.tech
Junior

Was ist ein Deadlock bei der Arbeit mit Goroutine?

sobes.tech KI

Antwort von AI

Ein Deadlock ist eine Situation, in der zwei oder mehr Goroutinen sich gegenseitig blockieren, indem sie aufeinander warten, und keine von ihnen ihre Ausführung fortsetzen kann. Dies tritt am häufigsten bei der Arbeit mit Mutexes oder Kanälen auf.

Beispiele für Deadlocks:

  1. Mutex-Blockierung in unterschiedlicher Reihenfolge:

    // Beispiel für Deadlock mit Mutexes
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Sperrt mu1
        time.Sleep(100 * time.Millisecond) // Wartet kurz
        mu2.Lock() // Versucht, mu2 zu sperren (belegt durch goroutine2)
        fmt.Println("goroutine1 hat mu2 erworben")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Sperrt mu2
        time.Sleep(100 * time.Millisecond) // Wartet kurz
        mu1.Lock() // Versucht, mu1 zu sperren (belegt durch goroutine1)
        fmt.Println("goroutine2 hat mu1 erworben")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 hat mu1 gesperrt und wartet auf mu2. Goroutine2 hat mu2 gesperrt und wartet auf mu1. Beide Goroutinen sind blockiert.

  2. Senden in einen unpufferierten Kanal ohne Empfänger:

    // Beispiel für Deadlock mit Kanal
    func main() {
        ch := make(chan int)
        //go func() { // Wenn keine Goroutine zum Lesen gestartet wird, tritt Deadlock auf
        //	<-ch
        //}()
        ch <- 1 // Senden in den Kanal ohne aktiven Empfänger
        fmt.Println("Gesendet an Kanal") // Diese Zeile wird nicht erreicht
    }
    

    Das Senden in einen unpufferierten Kanal blockiert, bis eine andere Goroutine beginnt, daraus zu lesen. Wenn kein Empfänger vorhanden ist, tritt Deadlock auf.

  3. Lesen aus einem Kanal ohne Sender (nach Schließen oder mit leerem Puffer):

    // Beispiel für Deadlock beim Lesen aus einem Kanal
    func main() {
        ch := make(chan int, 1) // Pufferkanal mit 1 Element
        ch <- 1 // Senden
        close(ch) // Kanal schließen
        <-ch // 1 gelesen
        <-ch // Versuch, erneut aus einem geschlossenen und leeren Kanal zu lesen - Deadlock
    }
    

    Das Lesen aus einem geschlossenen und nicht leeren Kanal endet erfolgreich. Das Lesen aus einem geschlossenen und leeren Kanal oder aus einem offenen, aber leeren Kanal ohne Sender führt zu Blockierung.

Die Laufzeitumgebung von Golang kann bestimmte Deadlock-Typen erkennen (z.B. wenn alle blockierten Goroutinen auf das Empfangen oder Senden an eine einzelne Goroutine warten), und in diesem Fall beendet sich das Programm abrupt mit der Meldung "fatal error: all goroutines are asleep - deadlock!". Allerdings können nicht alle Deadlocks automatisch erkannt werden.

Um Deadlocks zu vermeiden, ist es wichtig:

  • Einen konsistenten Reihenfolge beim Sperren von Mutexes einzuhalten.
  • Sicherzustellen, dass für jeden Send in einen unpufferierten Kanal eine entsprechende Leseoperation vorhanden ist, und umgekehrt.
  • Bei der Arbeit mit Kanälen select mit default-Zweig oder Timeouts zu verwenden, wenn nötig.
  • Die Lebensdauer der Goroutinen und das Schließen der Kanäle sorgfältig zu verwalten.