Dans l’exemple précédent nous avons vu comment gérer des compteurs d’état simples avec des opérations atomiques. Pour des états plus compliqués, on peut utiliser un _mutex_ pour accéder de manière sûre à des données à travers plusieurs goroutines. |
|
package main
|
|
import (
"fmt"
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
)
|
|
func main() {
|
|
Pour notre exemple, l’état |
var state = make(map[int]int)
|
Ce |
var mutex = &sync.Mutex{}
|
Pour comparer l’approche avec des mutexes avec une autre que nous verrons plus tard, |
var ops int64 = 0
|
Ici on lance 100 goroutines pour exécuter des lectures répétées sur l’état. |
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
|
A chaque lecture, on sélectionne une
clé à laquelle on souhaite accéder,
on bloque le mutex avec |
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddInt64(&ops, 1)
|
Pour nous assurer que cette goroutine
ne prend pas toutes les ressources, on
rend la main explicitement après
chaque opération avec
|
runtime.Gosched()
}
}()
}
|
On démarre également 10 goroutines pour simuler des écritures, de la même manière que pour les écritures. |
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddInt64(&ops, 1)
runtime.Gosched()
}
}()
}
|
On fait travailler les 10 goroutines sur les
|
time.Sleep(time.Second)
|
On rapporte le nombre total d’opérations réalisées. |
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
|
Avec un verrou final sur le mutex de |
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
|
Lancer le programme montre qu’on a exécuté environ
3,500,000 d’opérations sur |
$ go run mutexes.go
ops: 3598302
state: map[1:38 4:98 2:23 3:85 0:44]
|
Ensuite nous verrons comment implémenter la même gestion d’état avec uniquement des goroutines et des canaux. |
Exemple suivant: Goroutines à états.