49 lines
1.5 KiB
Go
49 lines
1.5 KiB
Go
// The primary mechanism for managing state in Go is
|
|
// communication over channels. We saw this for example
|
|
// with [worker pools](worker-pool). There are a few other
|
|
// options for managing state though. Here we'll
|
|
// look at using the `sync/atomic` package for _atomic
|
|
// counters_ accessed by multiple goroutines.
|
|
|
|
package main
|
|
|
|
import "fmt"
|
|
import "time"
|
|
import "sync/atomic"
|
|
|
|
func main() {
|
|
|
|
// We'll use an unsigned integer to represent our
|
|
// (always-positive) counter.
|
|
var ops uint64 = 0
|
|
|
|
// To simulate concurrent updates, we'll start 50
|
|
// goroutines that each increment the counter about
|
|
// once a millisecond.
|
|
for i := 0; i < 50; i++ {
|
|
go func() {
|
|
for {
|
|
time.Sleep(time.Millisecond)
|
|
|
|
// To atomically increment the counter we
|
|
// use `AddUint64`, giving it the memory
|
|
// address of our `ops` counter with the
|
|
// `&` syntax.
|
|
atomic.AddUint64(&ops, 1)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Wait a second to allow some ops to accumulate.
|
|
time.Sleep(time.Second)
|
|
|
|
// In order to safely use the counter while it's still
|
|
// being updated by other goroutines, we extract a
|
|
// copy of the current value into `opsFinal` via
|
|
// `LoadUint64`. As above we need to give this
|
|
// function the memory address `&ops` from which to
|
|
// fetch the value.
|
|
opsFinal := atomic.LoadUint64(&ops)
|
|
fmt.Println("ops:", opsFinal)
|
|
}
|