Sobes.tech
Junior

Co je to deadlock při práci s goroutine?

sobes.tech AI

Odpověď od AI

Deadlock (zaseknutí) je situace, kdy dvě nebo více gorutin blokují navzájem, čekají na sebe, a žádná z nich nemůže pokračovat. Nejčastěji k tomu dochází při práci s mutexy nebo kanály.

Příklady deadlocku:

  1. Zámky mutexů v různém pořadí:

    // Příklad deadlocku s mutexy
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Získá mu1
        time.Sleep(100 * time.Millisecond) // Čeká trochu
        mu2.Lock() // Pokouší se získat mu2 (který drží goroutine2)
        fmt.Println("goroutine1 získal mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Získá mu2
        time.Sleep(100 * time.Millisecond) // Čeká trochu
        mu1.Lock() // Pokouší se získat mu1 (který drží goroutine1)
        fmt.Println("goroutine2 získal mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 získala mu1 a čeká na mu2. Goroutine2 získala mu2 a čeká na mu1. Obě gorutiny jsou zablokované.

  2. Odeslání do nevybaveného kanálu bez příjemce:

    // Příklad deadlocku s kanálem
    func main() {
        ch := make(chan int)
        //go func() { // Pokud nespustíme gorutinu pro čtení, dojde k deadlocku
        //	<-ch
        //}()
        ch <- 1 // Odeslání do kanálu bez aktivního příjemce
        fmt.Println("Odesláno do kanálu") // Tento řádek nebude dosažen
    }
    

    Odeslání do nevybaveného kanálu se zablokuje, dokud jiná gorutina nezačne číst z něj. Pokud není žádný příjemce, dojde k deadlocku.

  3. Čtení z kanálu bez odesílatele (po uzavření nebo prázdný bufferovaný):

    // Příklad deadlocku při čtení z kanálu
    func main() {
        ch := make(chan int, 1) // Bufferovaný kanál na 1 prvek
        ch <- 1 // Odeslali jsme 1
        close(ch) // Uzavřeli kanál
        <-ch // Přečetli 1
        <-ch // Pokoušíme se přečíst znovu z uzavřeného a prázdného kanálu - deadlock
    }
    

    Čtení z uzavřeného nevybaveného kanálu skončí úspěšně. Čtení z uzavřeného a prázdného nebo z otevřeného, ale prázdného nevybaveného kanálu bez odesílatele povede k blokování.

Golang runtime může detekovat určité typy deadlocků (například když všechny zablokované gorutiny čekají na příjem nebo odeslání jediného gorutiny), a v takovém případě program neočekávaně skončí s hlášením "fatal error: all goroutines are asleep - deadlock!". Nicméně, ne všechny deadlocky mohou být automaticky detekovány.

Pro prevenci deadlocků je důležité:

  • Dodržovat konzistentní pořadí získávání mutexů.
  • Zajistit, že pro každé odeslání do nevybaveného kanálu existuje odpovídající příjem, a naopak.
  • Při práci s kanály používat select s default větví nebo časové limity podle potřeby.
  • Pečlivě řídit životnost gorutin a uzavírání kanálů.