GO by example, part 4
This commit is contained in:
90
5-go-by-example/40-stateful-goroutines.go
Normal file
90
5-go-by-example/40-stateful-goroutines.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// built-in synchronization features of goroutines and channels
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// state will be owned by a single goroutine
|
||||
// this will guarantee that the data is never corrupted with concurrent access
|
||||
// in order to read or write that state, other goroutines will send messages to the owning goroutine and receive corresponding replies
|
||||
// these readOp and writeOp structs encapsulate those requests and a way for the owning goroutine to respond
|
||||
type readOp struct {
|
||||
key int
|
||||
resp chan int
|
||||
}
|
||||
type writeOp struct {
|
||||
key int
|
||||
val int
|
||||
resp chan bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
// count how many operations to perform
|
||||
var readOps uint64
|
||||
var writeOps uint64
|
||||
|
||||
// reads and writes channels will be used by other goroutines
|
||||
reads := make(chan readOp)
|
||||
writes := make(chan writeOp)
|
||||
|
||||
// goroutine that owns the state, which is a map as in the previous example but now private to the stateful goroutine
|
||||
// this goroutine repeatedly selects on the reads and writes channels, responding to requests as they arrive
|
||||
go func() {
|
||||
var state = make(map[int]int)
|
||||
for {
|
||||
select {
|
||||
case read := <-reads:
|
||||
read.resp <- state[read.key]
|
||||
case write := <-writes:
|
||||
state[write.key] = write.val
|
||||
write.resp <- true
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// starts 100 goroutines to issue reads to the state-owning goroutine via the reads channel
|
||||
for r := 0; r < 100; r++ {
|
||||
go func() {
|
||||
for {
|
||||
// each read requires constructing a readOp, sending it over the reads channel, and the receiving the result over the provided resp channel
|
||||
read := readOp{
|
||||
key: rand.Intn(5),
|
||||
resp: make(chan int)}
|
||||
reads <- read
|
||||
<-read.resp
|
||||
atomic.AddUint64(&readOps, 1)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// start 10 writes as well, using a similar approach
|
||||
for w := 0; w < 10; w++ {
|
||||
go func() {
|
||||
for {
|
||||
write := writeOp{
|
||||
key: rand.Intn(5),
|
||||
val: rand.Intn(100),
|
||||
resp: make(chan bool)}
|
||||
writes <- write
|
||||
<-write.resp
|
||||
atomic.AddUint64(&writeOps, 1)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// let the goroutines work for a second
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// capture and report the op counts
|
||||
readOpsFinal := atomic.LoadUint64(&readOps)
|
||||
fmt.Println("readOps:", readOpsFinal)
|
||||
writeOpsFinal := atomic.LoadUint64(&writeOps)
|
||||
fmt.Println("writeOps:", writeOpsFinal)
|
||||
}
|
||||
Reference in New Issue
Block a user