В предыдущем примере мы увидели, как управлять простым состоянием счетчика с помощью атомарных операций. Для более сложного состояния мы можем использовать мьютекс(http://en.wikipedia.org/wiki/Mutual_exclusion) для безопасного доступа к данным в нескольких горутинах. |
|
package main
|
|
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
|
|
func main() {
|
|
Для нашего примера |
var state = make(map[int]int)
|
Этот |
var mutex = &sync.Mutex{}
|
Мы будем отслеживать, сколько операций чтения и записи мы выполняем. |
var readOps uint64
var writeOps uint64
|
Здесь мы запускаем 100 горутин для выполнения повторных операций чтения по состоянию, один раз в миллисекунду в каждой горутине. |
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
|
Для каждого чтения мы выбираем ключ для
доступа, блокируем |
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddUint64(&readOps, 1)
|
Немного ждем между чтениями. |
time.Sleep(time.Millisecond)
}
}()
}
|
Запустим так же 10 горутин для симуляции записи, так же как мы делали для чтения. |
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.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
|
Пусть 10 горутин работают над |
time.Sleep(time.Second)
|
Смотрим финальное количество операций |
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
|
С окончательной блокировкой |
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
|
Запуск программы показывает, что мы выполнили
около 90000 операций в нашем синхронизированном
с |
$ go run mutexes.go
readOps: 83285
writeOps: 8320
state: map[1:97 4:53 0:33 2:15 3:2]
|
Далее мы рассмотрим реализацию той же задачи управления состоянием с использованием только горутин и каналов. |
Следующий пример: Управление состоянием горутин (Stateful Goroutines).