Sobes.tech
Junior

Kas yra deadlock dirbant su goroutine?

sobes.tech AI

Atsakymas iš AI

Deadlock (užstrigimas) — tai situacija, kai dvi ar daugiau gorutinos blokuoja viena kitą, laukdamos viena kitos, ir nė viena jų negali tęsti vykdymo. Dažniausiai tai įvyksta dirbant su mutexais arba kanalais.

Deadlock pavyzdžiai:

  1. Mutexų užrakinimas skirtinga tvarka:

    // Deadlock pavyzdys su mutexais
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Užrakina mu1
        time.Sleep(100 * time.Millisecond) // Trumpo laiko laukimas
        mu2.Lock() // Bando užrakinti mu2 (kurį laiko goroutine2)
        fmt.Println("goroutine1 įgijo mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Užrakina mu2
        time.Sleep(100 * time.Millisecond) // Trumpo laiko laukimas
        mu1.Lock() // Bando užrakinti mu1 (kurį laiko goroutine1)
        fmt.Println("goroutine2 įgijo mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 įgijo mu1 ir laukia mu2. Goroutine2 įgijo mu2 ir laukia mu1. Abi gorutinos yra užblokuotos.

  2. Žinutės siuntimas nebuferizuotu kanalu be gavėjo:

    // Deadlock pavyzdys su kanalu
    func main() {
        ch := make(chan int)
        //go func() { // Jei neužvesime skaitymo gorutinos, įvyks deadlock
        //	<-ch
        //}()
        ch <- 1 // Siuntimas į kanalą be aktyvaus gavėjo
        fmt.Println("Išsiųsta į kanalą") // Šis eilutė nebus pasiekta
    }
    

    Siuntimas nebuferizuotu kanalu blokuos, kol kita gorutina pradės skaityti iš jo. Jei nėra gavėjo, įvyksta deadlock.

  3. Skaitymas iš kanalo be siuntėjo (po uždarymo arba tuščio buferio):

    // Deadlock pavyzdys skaitant iš kanalo
    func main() {
        ch := make(chan int, 1) // Buferuotas kanalas su 1 elementu
        ch <- 1 // Siuntėme 1
        close(ch) // Uždarėme kanalą
        <-ch // Perskaitėme 1
        <-ch // Bandome dar kartą skaityti iš uždaryto ir tuščio kanalo - deadlock
    }
    

    Skaitymas iš uždaryto, tuščio kanalo bus sėkmingas. Skaitymas iš uždaryto ir tuščio arba atviro, bet tuščio nebuferizuoto kanalo be siuntėjo sukels blokavimą.

Golang vykdymo aplinka gali aptikti tam tikrus deadlock tipus (pavyzdžiui, kai visi užblokuoti gorutinos laukia vienos gorutinos priėmimo ar siuntimo), ir tokiu atveju programa netikėtai sustos su pranešimu "fatal error: all goroutines are asleep - deadlock!". Tačiau ne visi deadlockai gali būti automatiškai aptikti.

Deadlockų prevencijai svarbu:

  • Laikytis nuoseklaus mutexų įsigijimo tvarkos.
  • Užtikrinti, kad kiekvienas nebuferizuotas siuntimas turi atitinkamą skaitymą, ir atvirkščiai.
  • Dirbant su kanalais naudoti select su default grupe arba laiko apribojimais pagal poreikį.
  • Atsargiai valdyti gorutinos gyvavimo ciklą ir kanalų uždarymą.