Sobes.tech
Junior

Τι είναι το deadlock κατά την εργασία με goroutine;

sobes.tech AI

Απάντηση από AI

Ο deadlock (αδιέξοδο) είναι μια κατάσταση όπου δύο ή περισσότερες goroutines μπλοκάρουν η μία την άλλη περιμένοντας, και καμία από αυτές δεν μπορεί να συνεχίσει την εκτέλεση. Συχνότερα συμβαίνει κατά την εργασία με 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. Αποστολή σε μη-buffered κανάλι χωρίς δέκτη:

    // Παράδειγμα deadlock με κανάλι
    func main() {
        ch := make(chan int)
        //go func() { // Αν δεν ξεκινήσει μια goroutine ανάγνωσης, θα προκύψει deadlock
        //	<-ch
        //}()
        ch <- 1 // Αποστολή στο κανάλι χωρίς ενεργό δέκτη
        fmt.Println("Αποστολή στο κανάλι") // Αυτή η γραμμή δεν θα επιτευχθεί
    }
    

    Η αποστολή σε μη-buffered κανάλι μπλοκάρει μέχρι να ξεκινήσει μια goroutine να διαβάσει από αυτό. Αν δεν υπάρχει δέκτης, προκύπτει deadlock.

  3. Ανάγνωση από κανάλι χωρίς αποστολέα (μετά το κλείσιμο ή με άδειο buffer):

    // Παράδειγμα deadlock κατά την ανάγνωση από κανάλι
    func main() {
        ch := make(chan int, 1) // Buffer κανάλι με 1 στοιχείο
        ch <- 1 // Αποστολή
        close(ch) // Κλείσιμο καναλιού
        <-ch // Ανάγνωση 1
        <-ch // Προσπάθεια ανάγνωσης ξανά από κλειστό και άδειο κανάλι - deadlock
    }
    

    Η ανάγνωση από κλειστό και μη άδειο κανάλι θα ολοκληρωθεί με επιτυχία. Η ανάγνωση από κλειστό και άδειο κανάλι ή από ανοιχτό αλλά άδειο κανάλι χωρίς αποστολέα θα προκαλέσει μπλοκάρισμα.

Το runtime της Golang μπορεί να ανιχνεύσει ορισμένους τύπους deadlock (π.χ., όταν όλες οι μπλοκαρισμένες goroutines περιμένουν να λάβουν ή να στείλουν σε μια μόνο goroutine), και σε αυτή την περίπτωση το πρόγραμμα θα τερματίσει απότομα με το μήνυμα "fatal error: all goroutines are asleep - deadlock!". Ωστόσο, δεν μπορούν να ανιχνευθούν αυτόματα όλα τα deadlock.

Για την αποφυγή deadlock:

  • Να διατηρείτε μια συνεπή σειρά κατά την απόκτηση mutex.
  • Να διασφαλίζετε ότι κάθε αποστολή σε μη-buffered κανάλι έχει μια αντίστοιχη ανάγνωση, και το αντίστροφο.
  • Να χρησιμοποιείτε select με default ή χρονικά όρια όταν εργάζεστε με κανάλια, αν χρειάζεται.
  • Να διαχειρίζεστε προσεκτικά τον χρόνο ζωής των goroutines και το κλείσιμο των καναλιών.