Go в примери: Взаимно изключване

В предния пример разгледахме управлението на състоянието на прости броячи с помощтта на неделими (единични) действия. За по-сложни случаи можем да ползваме превключвач1, за безопасен достъп до данни от множество гозадачи.


  1. mutex – превключвач. Съкращение от mutual exclusion – взаимно изключване.
package main
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.


  1. defer – отлагам, отсрочвам (действие)
    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 и две от тях достъпват същия брояч.


  1. concurrently – в съревнование, едновременно
    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]

В следващия пример ще видим как се осъществява същото това управление на състоянието само чрез гозадачи и канали.

Следващ пример: .