diff --git a/examples/atomic-counters/atomic-counters.go b/examples/atomic-counters/atomic-counters.go index 9df3276..2a0901b 100644 --- a/examples/atomic-counters/atomic-counters.go +++ b/examples/atomic-counters/atomic-counters.go @@ -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) } diff --git a/examples/atomic-counters/atomic-counters.hash b/examples/atomic-counters/atomic-counters.hash index c1b531a..e35f8f2 100644 --- a/examples/atomic-counters/atomic-counters.hash +++ b/examples/atomic-counters/atomic-counters.hash @@ -1,2 +1,2 @@ -a4190094ea0405b5f2733101beb97939a1d43aee -KDr9EMMPMgi +103c9b7d036e3a5c14dc481755b78b10dc9f894e +GRkVf6J1--B diff --git a/examples/atomic-counters/atomic-counters.sh b/examples/atomic-counters/atomic-counters.sh index e4523f9..1680a10 100644 --- a/examples/atomic-counters/atomic-counters.sh +++ b/examples/atomic-counters/atomic-counters.sh @@ -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. diff --git a/public/atomic-counters b/public/atomic-counters index b2e006e..6c9d94c 100644 --- a/public/atomic-counters +++ b/public/atomic-counters @@ -46,7 +46,7 @@ counters accessed by multiple goroutines.
package main
import "fmt"
-import "time"
-import "sync/atomic"
+ import (
+ "fmt"
+ "sync"
+ "sync/atomic"
+)
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++ {
- go func() {
- for {
+ wg.Add(1)
ops
counter with the
atomic.AddUint64(&ops, 1)
+ go func() {
+ for c := 0; c < 1000; c++ {
ops
counter with the
Wait a bit between increments.
- + time.Sleep(time.Millisecond)
+ atomic.AddUint64(&ops, 1)
}
+ wg.Done()
}()
}
@@ -144,31 +159,13 @@ address of our ops
counter with the
- 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.
+ Wait until all the goroutines are done.
- opsFinal := atomic.LoadUint64(&ops)
- fmt.Println("ops:", opsFinal)
+ wg.Wait()
+ fmt.Println("ops:", ops)
}
@@ -181,14 +178,18 @@ fetch the value.
- 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
@@ -219,7 +220,7 @@ state.