Go podle Příkladů: Atomické countery

Primární mechanismus pro správu stavů je v Go komunikace pomocí kanálů. To jsme viděli v příkladu se zásobníky pracovních rutin. Ale existují ještě další možnosti pro správu stavu. Tady se podíváme na použití atomických counterů z balíčku sync/atomic které jsou používané zároveň několika gorutinami.

package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
func main() {

Použijeme atomický celočíselný typ pro reprezentaci našeho (vždy kladného) counteru.

    var ops atomic.Uint64

WaitGroup nám pomůže počkat, až všechny gorutiny dokončí svou práci.

    var wg sync.WaitGroup

Nastartujeme 50 gorutin, z nichž každá zvýší counter přesně 1000 krát.

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

Pro atomické zvýšení counteru použijeme Add.

                ops.Add(1)
            }
        })
    }

Počkáme, až budou všechny gorutiny hotové.

    wg.Wait()

Tady už do ops žádná gorutina nezapisuje, ale pomocí Load je bezpečné atomicky číst hodnotu i ve chvíli, kdy ji jiné gorutiny (atomicky) mění.

    fmt.Println("ops:", ops.Load())
}

Očekáváme přesně 50 000 operací. Kdybychom použili neatomický integer a zvyšovali ho pomocí ops++, pravděpodobně bychom dostali jiné číslo, měnící se mezi jednotlivými spuštěními, protože by se gorutiny navzájem ovlivňovaly. Navíc bychom při spuštění s příznakem -race dostali chyby souběhu (data race).

$ go run atomic-counters.go
ops: 50000

Jako další se podíváme na mutexy, které jsou dalším nástrojem pro správu stavu.

Další příklad: Mutexy.