В предния пример разгледахме управлението на състоянието на прости броячи с помощтта на неделими (единични) действия. За по-сложни случаи можем да ползваме превключвач1, за безопасен достъп до данни от множество гозадачи.
import (
"fmt"
"sync"
)
Видът Container (съсъд) съдържа карта с броячи, counters.
Понеже искаме да можем да го обновяваме едновременно от
множество гозадачи, добавяме превключвач от вида
Mutex, за да съгласуваме достъпа до counters.
Отбележете си, че превключвачите не трябва да бъдат
копирани, така че ако тази структура бива подавана
насам-натам, то трябва да бъде подавана само като
указател.
type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
Заключваме достъпа до counters с помощта на
превключвача, преди да достъпим counters;
отключваме достъпа в края на функцията като
ползваме defer1.
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func main() {
c := Container{
Забележете, че нулевата стойност на превключвача е използваема още при самото му обявяване, сиреч, няма нужда от задаване на начална стойност.
counters: map[string]int{"a": 0, "b": 0},
}
var wg sync.WaitGroup
Тази функция увеличава стойността в именован брояч чрез повторение.
увеличи := func(name string, n int) {
for range n {
c.inc(name)
}
}
Пускаме няколко гозадачи в съревнование1.
Забележете, че всички те достъпват същия
Container и две от тях достъпват същия брояч.
wg.Go(func() {
увеличи("a", 10000)
})
wg.Go(func() {
увеличи("a", 10000)
})
wg.Go(func() {
увеличи("b", 10000)
})
Чакаме гозадачите да приключат.
wg.Wait()
fmt.Println(c.counters)
}
Като пуснем програмата, виждаме, че броячите са променени както очакваме.
$ go run mutexes.go
map[a:20000 b:10000]
В следващия пример ще видим как се осъществява същото това управление на състоянието само чрез гозадачи и канали.
Следващ пример: Гозадачи, запазващи състояние.