@@ -7,9 +7,11 @@
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "time"
|
||||
import "sync/atomic"
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -17,33 +19,28 @@ func main() {
|
||||
// (always-positive) counter.
|
||||
var ops uint64
|
||||
|
||||
// To simulate concurrent updates, we'll start 50
|
||||
// goroutines that each increment the counter about
|
||||
// once a millisecond.
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
// Wait a bit between increments.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Wait until all the goroutines are done.
|
||||
wg.Wait()
|
||||
fmt.Println("ops:", ops)
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
a4190094ea0405b5f2733101beb97939a1d43aee
|
||||
KDr9EMMPMgi
|
||||
103c9b7d036e3a5c14dc481755b78b10dc9f894e
|
||||
GRkVf6J1--B
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# Running the program shows that we executed about
|
||||
# 40,000 operations.
|
||||
# We expect to get exactly 50,000 operations. Had we
|
||||
# used the non-atomic `ops++` to increment the counter,
|
||||
# we'd likely get a different number, changing between
|
||||
# runs, because the goroutines would interfere with
|
||||
# each other. Moreover, we'd get data race failures
|
||||
# when running with the `-race` flag.
|
||||
$ go run atomic-counters.go
|
||||
ops: 41419
|
||||
ops: 50000
|
||||
|
||||
# Next we'll look at mutexes, another tool for managing
|
||||
# state.
|
||||
|
||||
Reference in New Issue
Block a user