Its not clear for newbies that using `time.Sleep` internally calls `runtime.Gosched()` Its better to use it directly, moreover on my machine it shows x175 improvement in speed (7m requests per second vs 40k).
50 lines
1.5 KiB
Go
50 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"
|
|
import "runtime"
|
|
|
|
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 {
|
|
// To atomically increment the counter we
|
|
// use `AddUint64`, giving it the memory
|
|
// address of our `ops` counter with the
|
|
// `&` syntax.
|
|
atomic.AddUint64(&ops, 1)
|
|
|
|
runtime.Gosched()
|
|
}
|
|
}()
|
|
}
|
|
|
|
// 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)
|
|
}
|