The primary mechanism for managing state in Go is communication over channels. We saw this for example with [worker pools](worker-pools). 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" |
||
"sync" |
||
"sync/atomic" |
||
) |
||
func main() { |
||
We'll use an unsigned integer to represent our (always-positive) counter. | var ops uint64 |
|
A WaitGroup will help us wait for all goroutines to finish their work. | var wg sync.WaitGroup |
|
We'll start 50 goroutines that each increment the counter exactly 1000 times. | for i := 0; i < 50; i++ { |
|
wg.Add(1) |
||
go func() { |
||
for c := 0; c < 1000; c++ { |
||
To atomically increment the counter we use `AddUint64`, giving it the memory address of our `ops` counter with the `&` syntax. | atomic.AddUint64(&ops, 1) |
|
} |
||
wg.Done() |
||
}() |
||
} |
||
Wait until all the goroutines are done. | wg.Wait() |
|
It's safe to access `ops` now because we know no other goroutine is writing to it. Reading atomics safely while they are being updated is also possible, using functions like `atomic.LoadUint64`. | fmt.Println("ops:", ops) |
ops: 50000 |
} |