Exploration du principe de fonctionnement des verrous dans Golang
En programmation simultanée, les verrous sont un mécanisme de synchronisation important utilisé pour protéger l'accès aux ressources partagées. Golang fournit une prise en charge du verrouillage via le package de synchronisation intégré, nous permettant de partager des données en toute sécurité entre plusieurs goroutines. Cet article approfondira le principe de fonctionnement des verrous dans Golang et l'expliquera avec des exemples de code spécifiques.
1. Verrou Mutex
Le type de verrou le plus basique dans Golang est le verrou mutex (Mutex), qui est représenté par la structure Mutex dans le package de synchronisation. Le principe d'un verrou mutex est simple : lorsqu'une goroutine accède à une ressource partagée, elle verrouille d'abord la ressource, et les autres goroutines doivent attendre que le verrou soit libéré avant de pouvoir y accéder. L'utilisation des verrous mutex est très simple. Il suffit d'appeler la méthode Lock() pour verrouiller la ressource et la méthode Unlock() pour libérer le verrou.
Ce qui suit est un exemple simple qui démontre le processus de deux goroutines accédant aux ressources partagées :
package main import ( "fmt" "sync" ) var count int var mutex sync.Mutex func main() { wg := sync.WaitGroup{} wg.Add(2) go increment() go increment() wg.Wait() fmt.Println("Final count:", count) } func increment() { for i := 0; i < 100000; i++ { mutex.Lock() count++ mutex.Unlock() } wg.Done() }
Dans l'exemple ci-dessus, nous avons défini un nombre de variables globales pour représenter les ressources partagées, et avons également défini un mutex de verrouillage mutex . Dans la fonction incrément() des deux goroutines, nous utilisons la méthode mutex.Lock() pour verrouiller le nombre de ressources partagées, puis appelons la méthode mutex.Unlock() pour libérer le verrou après avoir effectué l'opération count++. Enfin, nous utilisons sync.WaitGroup pour garantir que la valeur finale du décompte est imprimée après l'exécution des deux goroutines.
Le principe de fonctionnement du verrou mutex est très simple et clair. Il utilise le mécanisme de verrouillage et de déverrouillage pour garantir un accès sécurisé aux ressources partagées et éviter la concurrence des données.
2. Verrouillage en lecture-écriture
Dans certains scénarios, les verrouillages mutex entraîneront des goulots d'étranglement dans les performances. Si plusieurs goroutines lisent uniquement les ressources partagées sans écrire, il n'est pas nécessaire de les verrouiller. Afin d'améliorer les performances de concurrence, Golang fournit des verrous en lecture-écriture (RWMutex). Les verrous en lecture-écriture permettent à plusieurs goroutines de lire des ressources partagées en même temps, mais ils nécessitent un accès mutuellement exclusif lorsqu'il y a des opérations d'écriture.
L'utilisation des verrous en lecture-écriture est très simple et est représentée par la structure RWMutex dans le package de synchronisation. Lors de la lecture de ressources partagées, appelez la méthode RLock() pour ajouter un verrou en lecture, lors de l'écriture sur une ressource partagée, appelez la méthode Lock() pour ajouter un verrou en écriture, et lors de la libération du verrou, appelez RUnlock() et Unlock( ) méthodes respectivement.
Ce qui suit est un exemple simple qui démontre l'utilisation de verrous en lecture-écriture :
package main import ( "fmt" "sync" ) var count int var rwlock sync.RWMutex func main() { wg := sync.WaitGroup{} wg.Add(3) go increment() go readCount() go readCount() wg.Wait() } func increment() { for i := 0; i < 100000; i++ { rwlock.Lock() count++ rwlock.Unlock() } wg.Done() } func readCount() { rwlock.RLock() fmt.Println("Current count:", count) rwlock.RUnlock() wg.Done() }
Dans l'exemple ci-dessus, nous utilisons un nombre de variables globales pour représenter les ressources partagées, et définissons également un rwlock de verrouillage en lecture-écriture. Dans la fonction incrément(), nous utilisons la méthode rwlock.Lock() pour ajouter le verrou en écriture, puis appelons la méthode rwlock.Unlock() pour libérer le verrou après avoir effectué l'opération count++. Dans la fonction readCount(), nous utilisons la méthode rwlock.RLock() pour ajouter le verrou de lecture, imprimer la valeur actuelle du nombre, puis appeler la méthode rwlock.RUnlock() pour libérer le verrou. Grâce à l'utilisation de verrous en lecture-écriture, nous pouvons obtenir plusieurs goroutines pour lire la valeur de count en même temps sans blocage, ce qui améliore considérablement la concurrence des opérations de lecture.
3. Variables de condition
En plus des verrous mutex et des verrous en lecture-écriture, Golang fournit également des variables de condition (Cond) pour optimiser davantage la programmation simultanée. Les variables de condition permettent à goroutine d'attendre lorsqu'une certaine condition est remplie, puis de poursuivre l'exécution jusqu'à ce que la condition change.
L'utilisation de variables de condition est très flexible et est représentée par la structure Cond dans le package de synchronisation. Nous pouvons attendre que la condition soit remplie en appelant la méthode Wait() de Cond et appeler la méthode Signal() ou la méthode Broadcast() de Cond pour réveiller la goroutine en attente.
Ce qui suit est un exemple simple qui démontre l'utilisation de variables de condition :
package main import ( "fmt" "sync" ) var count int var cond *sync.Cond func main() { cond = sync.NewCond(&sync.Mutex{}) wg := sync.WaitGroup{} wg.Add(3) go increment() go decrement() go waitCount() wg.Wait() } func increment() { for i := 0; i < 10; i++ { cond.L.Lock() count++ fmt.Println("Increment count to", count) cond.Signal() cond.L.Unlock() } wg.Done() } func decrement() { for i := 0; i < 5; i++ { cond.L.Lock() for count <= 0 { cond.Wait() } count-- fmt.Println("Decrement count to", count) cond.L.Unlock() } wg.Done() } func waitCount() { cond.L.Lock() for count < 5 { cond.Wait() } fmt.Println("Count reaches 5") cond.L.Unlock() wg.Done() }
Dans l'exemple ci-dessus, nous utilisons une variable globale count pour représenter les ressources partagées, et définissons également une variable de condition cond en appelant la méthode sync.NewCond() pour créer une variable de condition associée à un verrou mutex.
Dans la fonction incrément(), nous acquérons d'abord le verrou du mutex cond.L, puis effectuons l'opération count++, imprimons la valeur de comptage actuelle et enfin appelons la méthode cond.Signal() pour réveiller la goroutine en attente. Dans la fonction decrement(), nous acquérons d'abord le verrou du mutex cond.L, puis utilisons la boucle for pour déterminer si le nombre est inférieur ou égal à 0. Si tel est le cas, appelez la méthode cond.Wait() pour suspendez la goroutine actuelle et attendez que les conditions soient remplies. Lorsque le nombre est supérieur à 0, effectuez l'opération de comptage, imprimez la valeur de comptage actuelle et enfin relâchez le verrouillage mutex. Dans la fonction waitCount(), nous acquérons d'abord le verrou du mutex cond.L, puis utilisons la boucle for pour déterminer si le compte est inférieur à 5. Si tel est le cas, appelons la méthode cond.Wait() pour suspendre le courant. goroutine et attendez que les conditions soient remplies. Lorsque le nombre atteint 5, imprimez le message d'invite « Le nombre atteint 5 » et enfin relâchez le verrouillage mutex.
Grâce à l'utilisation de variables de condition, nous pouvons réaliser une communication inter-thread plus complexe que les verrous mutex et les verrous en lecture-écriture, et contrôler de manière plus flexible l'ordre d'exécution des goroutines.
Résumé :
Cet article explore en profondeur le principe de fonctionnement des verrous dans Golang, y compris l'utilisation de verrous mutex, de verrous en lecture-écriture et de variables de condition. Les verrous Mutex garantissent un accès sécurisé aux ressources partagées grâce au verrouillage et au déverrouillage. Les verrous en lecture-écriture améliorent les performances de concurrence grâce aux verrous en lecture et aux verrous en écriture qui permettent à goroutine d'attendre lorsqu'une certaine condition est remplie. Grâce à l'utilisation appropriée des verrous, nous pouvons améliorer les performances du programme et garantir que les ressources partagées sont correctement partagées entre plusieurs goroutines.
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!