Sobes.tech
Junior

Kas ir deadlock, strādājot ar goroutine?

sobes.tech AI

Atbilde no AI

Deadlock (saslēgšanās) ir situācija, kad divas vai vairāk gorutinas bloķē viena otru, gaidot viena otru, un neviena no tām nevar turpināt izpildi. Visbiežāk tas notiek, strādājot ar mutexiem vai kanāliem.

Deadlock piemēri:

  1. Mutexu bloķēšana dažādā secībā:

    // Deadlock piemērs ar mutexiem
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Iegūst mu1
        time.Sleep(100 * time.Millisecond) // Mazs gaidīšanas laiks
        mu2.Lock() // Mēģina iegūt mu2 (ko tur goroutine2)
        fmt.Println("goroutine1 ieguva mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Iegūst mu2
        time.Sleep(100 * time.Millisecond) // Mazs gaidīšanas laiks
        mu1.Lock() // Mēģina iegūt mu1 (ko tur goroutine1)
        fmt.Println("goroutine2 ieguva mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 ieguva mu1 un gaida mu2. Goroutine2 ieguva mu2 un gaida mu1. Abi gorutinas ir bloķētas.

  2. Sūtīšana nebuferētā kanālā bez saņēmēja:

    // Deadlock piemērs ar kanālu
    func main() {
        ch := make(chan int)
        //go func() { // Ja neuzsāksim lasīšanas gorutinu, būs deadlock
        //	<-ch
        //}()
        ch <- 1 // Sūtīšana kanālā bez aktīva saņēmēja
        fmt.Println("Nosūtīts kanālā") // Šī rinda netiks sasniegta
    }
    

    Sūtīšana nebuferētā kanālā bloķēs, līdz cita gorutina sāks lasīt no tā. Ja saņēmējs nav, notiks deadlock.

  3. Lasīšana no kanāla bez sūtītāja (pēc slēgšanas vai tukša bufera):

    // Deadlock piemērs ar kanāla lasīšanu
    func main() {
        ch := make(chan int, 1) // Buferēts kanāls ar 1 elementu
        ch <- 1 // Nosūtījām 1
        close(ch) // Aizvērām kanālu
        <-ch // Lasījām 1
        <-ch // Mēģinām vēlreiz lasīt no slēgta un tukša kanāla - deadlock
    }
    

    Lasīšana no slēgta, tukša vai atvērta, bet tukša nebuferizēta kanāla bez sūtītāja noved pie bloķēšanas.

Golang rīks var atpazīt noteiktus deadlock veidus (piemēram, kad visi bloķētie gorutinas gaida vienas gorutinas saņemšanu vai sūtīšanu), un šādā gadījumā programma negaidīti pārtrauks ar ziņojumu "fatal error: all goroutines are asleep - deadlock!". Tomēr ne visi deadlock veidi tiek automātiski atpazīti.

Lai novērstu deadlock, ir svarīgi:

  • Saglabāt konsekventu mutexu iegūšanas kārtību.
  • Nodrošināt, ka katram nebuferētam sūtījumam ir atbilstoša lasīšana, un otrādi.
  • Darbā ar kanāliem izmantot select ar default daļu vai laika ierobežojumiem pēc nepieciešamības.
  • Rūpīgi pārvaldīt gorutinu dzīves ciklu un kanālu aizvēršanu.