Est-il sécuritaire de déverrouiller un mutex deux fois? Mon code:
var m sync.RWMutex = sync.RWMutex{}
func Read() {
m.RLock()
defer m.RUnlock()
// Do something that needs lock
err := SomeFunction1()
if err != nil {
return
}
m.RUnlock()
// Do something that does not need lock
SomeFunction2()
}
J'ai besoin de différer m.RUnlock () pour le cas SomeFunction1 () renvoie une erreur. Mais lorsque SomeFunction1 () revient sans erreur, m sera déverrouillé deux fois par m.RUnlock () et différer m.RUnlock ( ) .
Est-il sûr de déverrouiller le mutex deux fois? Sinon, comment dois-je corriger mon code?
3 Réponses :
Le déverrouillage d'un Mutex déverrouillé provoquera une panique.
Vous pouvez simplement supprimer le différer et l'ajouter dans la condition if:
func Read() {
m.RLock()
err := SomeFunction1()
m.RUnlock()
if (err != nil) {
return
}
SomeFunction2()
}
Ou encore mieux (avec un impact mineur sur la lisibilité):
var m sync.RWMutex = sync.RWMutex{}
func Read() {
m.RLock()
// Do something that needs lock
err := SomeFunction1()
if (err != nil) {
m.RUnlock()
return
}
m.RUnlock()
// Do something that does not need lock
SomeFunction2()
}
De godoc:
Alors, ne faites pas cela.
La solution la plus simple est de ne pas utiliser le déverrouillage différé, mais de le déverrouiller à chaque sortie possible. Ou, vous pouvez aussi faire ceci, mais ce n'est pas facile à lire:
func Read() {
if err := func() {
m.RLock()
defer m.RUnlock()
// Do something that needs lock
err := SomeFunction1()
if err != nil {
return err
}(); err != nil {
return err
}
// Do something that does not need lock
SomeFunction2()
}
Est-il sûr de déverrouiller un mutex deux fois?
Non, vous ne devriez pas déverrouiller le mutex deux fois. Il s'agit d'une erreur d'exécution selon la documentation .
RUnlock annule un seul appel RLock; il n'affecte pas les autres lecteurs simultanés. C'est une erreur d'exécution si rw n'est pas verrouillé pour la lecture à l'entrée de RUnlock.
Sinon, comment dois-je corriger mon code?
Je recommanderais de conserver le
différermais uniquementm.RUnlock ()en cas d'erreur. Cela peut facilement évoluer au cas où vous ajouteriez plus d'appels de fonction entreSomeFunction1 ()etSomeFunction2().func Read() { var err error m.RLock() defer func() { if err != nil { m.RUnlock() } }() // Do something that needs lock err = SomeFunction1() if err != nil { return } m.RUnlock() // Do something that does not need lock SomeFunction2() }Essayez sur Go Playground !
Notez que lorsque vous avez envie de faire cela (verrouiller et déverrouiller un mutex à divers points étranges d'une fonction), c'est généralement un signe que votre code n'est pas vraiment correctement structuré. Il y a cependant des cas étranges où cela est approprié.