Sobes.tech
Junior

Mi az a deadlock a goroutine használata közben?

sobes.tech MI

Válasz az MI-től

Deadlock (akadály) — olyan helyzet, amikor két vagy több gorutina blokkol egymás várásában, és egyik sem tud továbbhaladni. Leggyakrabban mutexek vagy csatornák használata közben fordul elő.

Deadlock példák:

  1. Mutexek blokkolása különböző sorrendben:

    // Deadlock példa mutexekkel
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // megszerzi mu1-et
        time.Sleep(100 * time.Millisecond) // vár egy kicsit
        mu2.Lock() // megpróbálja megszerezni mu2-t (amelyet goroutine2 tart)
        fmt.Println("goroutine1 megszerezte mu2-t")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // megszerzi mu2-t
        time.Sleep(100 * time.Millisecond) // vár egy kicsit
        mu1.Lock() // megpróbálja megszerezni mu1-et (amelyet goroutine1 tart)
        fmt.Println("goroutine2 megszerezte mu1-et")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 megszerezte a mu1-et és vár a mu2-re. Goroutine2 megszerezte a mu2-t és vár a mu1-re. Mindkét gorutina blokkolva van.

  2. Üzenet küldése nem pufferelt csatornára anélkül, hogy fogadó lenne:

    // Deadlock példa csatornával
    func main() {
        ch := make(chan int)
        //go func() { // Ha nem indítunk olvasó gorutint, deadlock lesz
        //	<-ch
        //}()
        ch <- 1 // Küldés a csatornára anélkül, hogy fogadó lenne
        fmt.Println("Küldve a csatornára") // Ez a sor nem fog elérni
    }
    

    A nem pufferelt csatornára való küldés blokkol, amíg másik gorutina nem kezd olvasni róla. Ha nincs olvasó, deadlock történik.

  3. Olvasás csatornáról fogadó nélkül (zárás vagy üres pufferelt csatorna után):

    // Deadlock példa csatornáról olvasáskor
    func main() {
        ch := make(chan int, 1) // Pufferelt csatorna 1 elemmel
        ch <- 1 // Küldött egyet
        close(ch) // Bezárta a csatornát
        <-ch // Olvasott egyet
        <-ch // Újra próbál olvasni a zárt és üres csatornáról - deadlock
    }
    

    A zárt, nem üres csatornáról való olvasás sikeres lesz. A zárt és üres, vagy nyitott, de üres nem pufferelt csatornáról való olvasás blokkolást eredményez.

A Golang futási környezet képes bizonyos deadlock típusokat felismerni (például amikor minden blokkolt gorutina vár egyetlen gorutina fogadására vagy küldésére), és ilyen esetben a program váratlanul leáll a "fatal error: all goroutines are asleep - deadlock!" üzenettel. Azonban nem minden deadlock automatikusan észlelhető.

A deadlockok megelőzése érdekében fontos:

  • Következetes mutex fogási sorrend betartása.
  • Biztosítani, hogy minden nem pufferelt csatornába küldéshez tartozik megfelelő olvasás, és fordítva.
  • Csatornákkal való munka során select-et használni default ággal vagy időkorlátokat alkalmazni szükség szerint.
  • Gondosan kezelni a gorutinok életciklusát és a csatornák bezárását.