Sobes.tech
Junior

Qu'est-ce qu'un deadlock lors de l'utilisation de goroutine?

sobes.tech IA

Réponse de l'IA

Un deadlock (impasse) est une situation où deux ou plusieurs goroutines se bloquent mutuellement en attendant l'une l'autre, et aucune ne peut continuer son exécution. Cela se produit le plus souvent lors de l'utilisation de mutex ou de canaux.

Exemples de deadlock :

  1. Blocage de mutex dans un ordre différent :

    // Exemple de deadlock avec mutex
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Bloque mu1
        time.Sleep(100 * time.Millisecond) // Attend un peu
        mu2.Lock() // Essaie de bloquer mu2 (occupé par goroutine2)
        fmt.Println("goroutine1 a acquis mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Bloque mu2
        time.Sleep(100 * time.Millisecond) // Attend un peu
        mu1.Lock() // Essaie de bloquer mu1 (occupé par goroutine1)
        fmt.Println("goroutine2 a acquis mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 a bloqué mu1 et attend mu2. Goroutine2 a bloqué mu2 et attend mu1. Les deux goroutines sont bloquées.

  2. Envoi dans un canal non tamponné sans récepteur :

    // Exemple de deadlock avec canal
    func main() {
        ch := make(chan int)
        //go func() { // Si aucune goroutine de lecture n'est lancée, il y aura deadlock
        //	<-ch
        //}()
        ch <- 1 // Envoi dans le canal sans récepteur actif
        fmt.Println("Envoyé au canal") // Cette ligne ne sera pas atteinte
    }
    

    L'envoi dans un canal non tamponné se bloque jusqu'à ce qu'une autre goroutine commence à lire. S'il n'y a pas de récepteur, cela provoque un deadlock.

  3. Lecture d'un canal sans émetteur (après fermeture ou avec tampon vide) :

    // Exemple de deadlock lors de la lecture d'un canal
    func main() {
        ch := make(chan int, 1) // Canal tamponné avec 1 élément
        ch <- 1 // Envoi
        close(ch) // Fermeture du canal
        <-ch // Lecture de 1
        <-ch // Tentative de lecture à nouveau d'un canal fermé et vide - deadlock
    }
    

    La lecture d'un canal fermé et non vide se termine avec succès. La lecture d'un canal fermé et vide ou d'un canal ouvert mais vide sans émetteur provoquera un blocage.

Le runtime de Golang peut détecter certains types de deadlocks (par exemple, lorsque toutes les goroutines bloquées attendent de recevoir ou d'envoyer à une seule goroutine), et dans ce cas, le programme se terminera brutalement avec le message "fatal error: all goroutines are asleep - deadlock!". Cependant, tous les deadlocks ne peuvent pas être détectés automatiquement.

Pour prévenir les deadlocks, il est important de :

  • Maintenir un ordre cohérent dans l'acquisition des mutex.
  • S'assurer que chaque envoi dans un canal non tamponné a une lecture correspondante, et vice versa.
  • Utiliser select avec une branche default ou des timeouts lors de l'utilisation de canaux si nécessaire.
  • Gérer soigneusement la durée de vie des goroutines et la fermeture des canaux.