Sobes.tech
Junior

რა არის deadlock როდესაც მუშაობთ goroutine-თან?

sobes.tech AI

პასუხი AI-სგან

Deadlock (deadlock) — դա իրավիճակ է, երբ երկու կամ ավելի goroutine-ներ խափանում են միմյանց սպասելով, և ոչ մեկը չի կարող շարունակել իր կատարումը: Ամենից հաճախ դա տեղի է ունենում mutex-ների կամ ալիքների հետ աշխատելիս:

Deadlock-ի օրինակներ:

  1. Mutex-ների խափանում տարբեր կարգով:

    // Deadlock-ի օրինակ mutex-ներով
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Հավաքում է mu1
        time.Sleep(100 * time.Millisecond) // Կարճ սպասում
        mu2.Lock() // Փորձում է հավաքել mu2 (որը պահում է goroutine2)
        fmt.Println("goroutine1 ձեռք է բերել mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Զգում է mu2
        time.Sleep(100 * time.Millisecond) // Կարճ սպասում
        mu1.Lock() // Փորձում է հավաքել mu1 (որը պահում է goroutine1)
        fmt.Println("goroutine2 ձեռք է բերել mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1-ը ձեռք է բերել mu1 և սպասում է mu2-ին: Goroutine2-ը ձեռք է բերել mu2 և սպասում է mu1-ին: Երկուսն էլ խափանված են:

  2. Նամակ ուղարկում ոչ բուֆերային ալիքին առանց ընդունողի:

    // Deadlock-ի օրինակ ալիքով
    func main() {
        ch := make(chan int)
        //go func() { // Եթե չսկսենք ընթերցող goroutine-ը, կլինի deadlock
        //	<-ch
        //}()
        ch <- 1 // Ուղարկում ալիքին առանց ակտիվ ընդունողի
        fmt.Println("Ուղարկված ալիքին") // Այս տողը չի հասնի
    }
    

    Ուղարկումը ոչ բուֆերային ալիքին կխափանի, մինչև մյուս goroutine-ը սկսի կարդալ նրանից: Եթե ընդունող չկա, տեղի կունենա deadlock:

  3. Կարդալ ալիքից առանց ուղարկողի (փակվելու կամ դատարկ բուֆերային):

    // Deadlock-ի օրինակ ալիքից կարդալիս
    func main() {
        ch := make(chan int, 1) // Բուֆերային ալիք 1 տարրով
        ch <- 1 // Ուղարկեցինք 1
        close(ch) // Փակեցինք ալիքին
        <-ch // Կարդացինք 1
        <-ch // Փորձում ենք կրկին կարդալ փակ և դատարկ ալիքից - deadlock
    }
    

    Կարդալը փակված, դատարկ կամ բաց ալիքից հաջող կլինի: Կարդալը փակ և դատարկ կամ բաց, բայց դատարկ ոչ բուֆերային ալիքից առանց ուղարկողի հանգեցնում է բլոկի:

Golang ռունթայմը կարող է որոշակի deadlock-ների տեսակներ հայտնաբերել (օրինակ, երբ բոլոր խափանված goroutine-ներ սպասում են միայն մեկ goroutine- ի ընդունմանը կամ ուղարկմանը), և այդ դեպքում ծրագիրը կավարտվի սխալի հաղորդագրությամբ "fatal error: all goroutines are asleep - deadlock!": Սակայն, ոչ բոլոր deadlock-ները կարող են ավտոմատ հայտնաբերվել:

Deadlock-ների կանխարգելման համար կարևոր է:

  • Պահպանել կոնսիստենտ mutex-ների ձեռքբերման կարգը:
  • Համոզվել, որ յուրաքանչյուր ոչ բուֆերային ալիքին ուղարկման համար կա համապատասխան կարդալ, և հակառակը:
  • Կանալների հետ աշխատելիս օգտագործել selectdefault ճյուղով կամ ժամանակային սահմանափակումներով:
  • Կանոնավոր կառավարել goroutine-ների կյանքի ցիկլը և ալիքների փակումը։