Nell’esempio precedente abbiamo visto come gestire semplici addizioni utilizzando delle operazioni atomiche. Per una gestione dello stato più complessa usiamo le mutex per accedere con sicurezza a dati condivisi attraverso multiple goroutine. |
|
package main
|
|
import (
"fmt"
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
)
|
|
func main() {
|
|
Per il nostro esempio lo |
var stato = make(map[int]int)
|
Questa |
var mutex = &sync.Mutex{}
|
Per confrontare l’approccio basato sulle mutex
piuttosto che altri, |
var ops int64 = 0
|
Di seguito facciamo partire 100 goroutine che dovranno eseguire ripetute operazioni di lettura sullo stato. |
for r := 0; r < 100; r++ {
go func() {
totale := 0
for {
|
Per ogni lettura scegliamo una chiave
per accedere alla map, facciamo un
|
chiave := rand.Intn(5)
mutex.Lock()
totale += stato[chiave]
mutex.Unlock()
atomic.AddInt64(&ops, 1)
|
Per essere sicuri che questa goroutine
non blocchi lo scheduler, cediamo
esplicitamente il controllo dopo ogni
operazione utilizzando
|
runtime.Gosched()
}
}()
}
|
Faremo inoltre partire altre 10 goroutine per simulare la scrittura, con lo stesso pattern che abbiamo usato per la lettura. |
for w := 0; w < 10; w++ {
go func() {
for {
chiave := rand.Intn(5)
valore := rand.Intn(100)
mutex.Lock()
stato[chiave] = valore
mutex.Unlock()
atomic.AddInt64(&ops, 1)
runtime.Gosched()
}
}()
}
|
Lasciamo ora lavorare le 10 goroutine per un secondo. |
time.Sleep(time.Second)
|
Prendiamo e stampiamo il numero di operazioni finale. |
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
|
Con un lock finale sullo |
mutex.Lock()
fmt.Println("stato:", stato)
mutex.Unlock()
}
|
Eseguendo il nostro programma vedremo che abbiamo
eseguito circa 4,000,000 di operazioni sul nostro
|
$ go run mutexes.go
ops: 4272750
stato: map[4:29 3:50 0:39 1:71 2:51]
|
Come prossima cosa, vedremo come come implementare questa gestione della memoria condivisa usando esclusivamente goroutine e channel. |
Prossimo esempio: Gestione dello Stato con Goroutine.