Go в примери: Неделими броячи

Главният начин за управление на състояние на данни в Го е чрез предаване на данни по канали. Видяхме как става това като разгледахме работилниците. Има и някои други възможности за управление на състоянието. Сега ще разгледаме употребата на пакета sync/atomic осъществяващ т. нар. неделими броячи1, като данни, предназначени за споделен достъп от множество съперничещи си гозадачи.


  1. atomic counters – неделими или единични (атомарни) броячи. Броячи, при които увеличаването на едно, достъпно за няколко гозадчи, число е неделимо единично действие от гледна точка на другите текущи гозадачи, съревноваващи се с текущата гозадача, да увеличат с единица същото споделено в паметта число. Вижте подробни обяснения на https://antonz.org/go-concurrency/atomics/ и https://preshing.com/20130618/atomic-vs-non-atomic-operations/
package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
func main() {

Ще използваме променлива стойност от вид неделимо цяло положително число, за нашия (винаги положителен) брояч.

    var действия atomic.Uint64

Ще си направим чакалня с WaitGroup, за да изчакаме всички задачи да приключат.

    var wg sync.WaitGroup

Ще пуснем 50 гозадачи, всяка от които ще увеличи стойността на брояча точно 1000 пъти.

    for range 50 {
        wg.Go(func() {
            for range 1000 {

За да увеличим стойността на брояча чрез неделимо действие, ползваме метода Add.

                действия.Add(1)
            }
        })
    }

Чакаме всички гозадачки да приключат.

    wg.Wait()

Тук вече никоя гозадаче не пише в действия, но благодарения на метода Load знаем, че действието зареждане (load) е неделимо, дори ако други задачи обновяваха стойността в действия.

    fmt.Println("действия:", действия.Load())
}

Очакваме програмата ни да изведе на екрана точно 50000 – броят извършени действия – увеличаване с единица. Ако бяхме използвали обикновено – не неделимо число, щяхме да видим друго число, което щеше даже да е различно при всяко пускане на програмата Причината щеше да се състои в това, че няколко гозадачи щяха да виждат една и съща стойност на числото едновременно. Също щяхме да получим и предупреждения за „съревнование“ (race), ако пуснехме програмата с флаг -race.

$ go run atomic-counters.go
действия: 50000

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

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