diff --git a/examples.txt b/examples.txt index ca93ee0..37d48c2 100644 --- a/examples.txt +++ b/examples.txt @@ -38,8 +38,9 @@ Timers Tickers Worker Pools Rate Limiting -# State Goroutine -# State Mutex +Atomic Counters +# Mutexs +# Stateful Goroutines Sorting Sorting by Functions # Collection Functions diff --git a/examples/atomic-counters/atomic-counters.go b/examples/atomic-counters/atomic-counters.go new file mode 100644 index 0000000..ca0b63a --- /dev/null +++ b/examples/atomic-counters/atomic-counters.go @@ -0,0 +1,48 @@ +// 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 simple +// 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) +} diff --git a/examples/atomic-counters/atomic-counters.sh b/examples/atomic-counters/atomic-counters.sh new file mode 100644 index 0000000..55f937a --- /dev/null +++ b/examples/atomic-counters/atomic-counters.sh @@ -0,0 +1,4 @@ +# Running the program shows that we executed about +# 40,000 operations. +$ go run atomic-counters.go +ops: 40200 diff --git a/examples/state-mutex/state-mutex.go b/examples/state-mutex/state-mutex.go index 08e8b74..c336be1 100644 --- a/examples/state-mutex/state-mutex.go +++ b/examples/state-mutex/state-mutex.go @@ -1,3 +1,5 @@ +// State + package main import "fmt" @@ -7,14 +9,6 @@ import "sync" import "sync/atomic" import "runtime" -func randKey() int { - return rand.Intn(10) -} - -func randVal() int { - return rand.Intn(100) -} - // Globally-accessible state. var data = make(map[int]int) @@ -27,7 +21,7 @@ var opCount int64 = 0 func generateReads() { total := 0 for { - key := randKey() + key := rand.Intn(10) dataMutex.Lock() total += data[key] dataMutex.Unlock() @@ -39,8 +33,8 @@ func generateReads() { // Generate random writes. func generateWrites() { for { - key := randKey() - val := randVal() + key := rand.Intn(10) + val := rand.Intn(100) dataMutex.Lock() data[key] = val dataMutex.Unlock() @@ -62,5 +56,3 @@ func main() { finalOpCount := atomic.LoadInt64(&opCount) fmt.Println(finalOpCount) } - -// todo: "State with Mutexes?"