Maison > développement back-end > Golang > Qu'est-ce qui fait que ma goroutine reste bloquée dans le code mutex suivant ?

Qu'est-ce qui fait que ma goroutine reste bloquée dans le code mutex suivant ?

王林
Libérer: 2024-02-13 18:57:07
avant
713 Les gens l'ont consulté

是什么导致我的 goroutine 在以下互斥体代码中陷入僵局?

L'éditeur PHP Xinyi est là pour répondre à une question courante : "Qu'est-ce qui fait que ma goroutine reste bloquée dans le code mutex suivant ? En programmation simultanée, l'utilisation de verrous mutex (Mutex) est courante. L'une des façons de résoudre concurrence pour les ressources partagées. Cependant, s'il y a des problèmes dans le code, la goroutine peut se retrouver bloquée et incapable de poursuivre son exécution. Ensuite, nous discuterons en détail des causes possibles de ce problème et proposerons des solutions.

Contenu de la question

J'essaie d'enregistrer une carte de clés, chaque clé a une serrure séparée. Lors de la création d'un verrou pour une clé spécifique, j'utilise un mutex global pour écrire sur la carte.

Une fois la création du verrou pour la clé terminée, j'utiliserai le nouveau verrou et je le libérerai une fois le travail terminé. Actuellement, j'essaie de modifier une seule clé pour tester mon code.

Voici le code :

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "sync"
    "time"
)

var count int
var globalMutex *sync.RWMutex
var mutexes map[int]*sync.Mutex

func MyLock(index int) {
    fmt.Println("Aquiring Lock")
    globalMutex.Lock()
    defer globalMutex.Unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.Mutex)
    }

    if _, ok := mutexes[index]; !ok {
        mutexes[index] = &sync.Mutex{}
    }

    fmt.Println("Aquiring 2nd Lock")
    mutexes[index].Lock()
    fmt.Println("Aquired Lock")
}

func MyUnlock(index int) {
    globalMutex.Lock()
    defer globalMutex.Unlock()
    mutexes[index].Unlock()
}

func main() {
    var wg sync.WaitGroup
    globalMutex = &sync.RWMutex{}
    wg.Add(500)
    for i := 0; i < 500; i++ {
        go func(i int) {
            defer wg.Done()

            MyLock(2)

            time.Sleep(1 * time.Second)
            fmt.Println(i)

            MyUnlock(2)
        }(i)
    }

    wg.Wait()
    fmt.Println(mutexes)
    fmt.Println(count)
}
Copier après la connexion

Je ne sais pas pourquoi il ne peut pas acquérir le verrou. Lien Playground : https://go.dev/play/p/-co0xaxpuy0

Solution

mylock peut verrouiller les mutex globaux et individuels. Cela rend le déverrouillage impossible dans certains cas :

  1. goroutine 1 appelle mylock(2). Cela provoque le verrouillage du verrou unique l2 comme prévu.
  2. goroutine 2 appelle mylock(2). Il acquiert le verrou global puis bloque en attendant que l2 soit libéré. Pendant l'attente, le verrouillage global continue d'être maintenu.
  3. goroutine 1 appelle myunlock(2). Il est bloqué en attendant que le verrou global (détenu par la goroutine 2) soit libéré. C'est une impasse.

Pour résoudre ce problème, remettez les verrous individuels tout en maintenant le verrou global au lieu de les (dé)verrouiller. La fonction myunlock devient inutile :

func mylock(index int) *sync.mutex {
    globalmutex.lock()
    defer globalmutex.unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.mutex)
    }

    mu := mutexes[index]
    if mu == nil {
        mu = &sync.mutex{}
        mutexes[index] = mu
    }

    return mu
}

func main() {
    var wg sync.waitgroup
    globalmutex = &sync.rwmutex{}
    wg.add(500)
    for i := 0; i < 500; i++ {
        go func(i int) {
            defer wg.done()

            mu := mylock(2)
            mu.lock()
            defer mu.unlock()

            time.sleep(1 * time.second)
            fmt.println(i)
        }(i)
    }

    wg.wait()
    fmt.println(mutexes)
    fmt.println(count)
}
Copier après la connexion

Pour améliorer les performances, vous pouvez d'abord vérifier si un seul verrou existe, tout en ne détenant qu'un verrou global en lecture (notez que cela change ce que représente count) :

func MyLock(index int) *sync.Mutex {
    globalMutex.RLock()
    mu := mutexes[index]
    globalMutex.RUnlock()

    if mu != nil {
        return mu
    }
    
    globalMutex.Lock()
    defer globalMutex.Unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.Mutex)
    }

    mu = mutexes[index] // have to check again because
    if mu == nil {      // we briefly released globalMutex 
        mu = &sync.Mutex{}
        mutexes[index] = mu
    }

    return mu
}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal