Skip to content

Commit ce59e92

Browse files
committed
41: Stateful Goroutines examples.
1 parent 99f2cbf commit ce59e92

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ go run . -module closingchannels
6060
* [38 - Rate Limiting](ratelimiting/main.go)
6161
* [39 - Atomic Counters](atomiccounters/main.go)
6262
* [40 - Mutexes](mutexes/main.go)
63+
* [41 - Stateful Goroutines](statefulgoroutines/main.go)

concurrency_patterns/.gitkeep

Whitespace-only changes.

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
"github.com/symonk/learning-golang/waitgroups"
4444
"github.com/symonk/learning-golang/workerpools"
4545
"github.com/symonk/learning-golang/mutexes"
46+
"github.com/symonk/learning-golang/statefulgoroutines"
4647
)
4748

4849
func main() {
@@ -110,5 +111,6 @@ func buildMap() map[string]func() {
110111
fnMap["ratelimiting"] = ratelimiting.Run
111112
fnMap["atomiccounters"] = atomiccounters.Run
112113
fnMap["mutexes"] = mutexes.Run
114+
fnMap["statefulgoroutines"] = statefulgoroutines.Run
113115
return fnMap
114116
}

statefulgoroutines/main.go

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,89 @@
11
package statefulgoroutines
22

3-
func Run() {}
3+
import (
4+
"math/rand"
5+
"time"
6+
"fmt"
7+
"sync/atomic"
8+
)
9+
10+
// Previously we looked at sharing complex state access
11+
// across goroutines using `mutexes`. A better solution
12+
// is to utilise stateful goroutines.
13+
func Run() {
14+
var readOperations uint64
15+
var writeOperations uint64
16+
17+
// Create two channels for managing read/write access accordingly.
18+
reads := make(chan ReadOperation)
19+
writes := make(chan WriteOperation)
20+
21+
// Create the goroutine, responsible for managing the state, a gate-keeper
22+
// This goroutine is responsible for monitoring the read/write channels
23+
// and maintaining state accordingly
24+
go func() {
25+
state := make(map[int]int)
26+
for {
27+
select {
28+
case read := <- reads:
29+
read.response <- state[read.key]
30+
case write := <- writes:
31+
state[write.key] = state[write.value]
32+
write.response <- true
33+
}
34+
}
35+
}()
36+
37+
// Lets concurrently read from many goros
38+
for r := 0; r < 100; r++ {
39+
// Each goroutine will indefinitely attempt to access the shared state
40+
// But via channel based communication to the gatekeeper.
41+
go func() {
42+
for {
43+
readOp := ReadOperation{
44+
key: rand.Intn(5),
45+
response: make(chan int)}
46+
reads <- readOp
47+
<- readOp.response
48+
atomic.AddUint64(&readOperations, 1)
49+
time.Sleep(time.Millisecond)
50+
51+
}
52+
}()
53+
}
54+
55+
// While we also concurrently write from other goros
56+
for w := 0; w < 10; w++ {
57+
go func() {
58+
for {
59+
writeOp := WriteOperation{
60+
key: rand.Intn(10),
61+
value: rand.Intn(10),
62+
response: make(chan bool),
63+
}
64+
writes <- writeOp
65+
<- writeOp.response
66+
atomic.AddUint64(&writeOperations, 1)
67+
time.Sleep(time.Millisecond)
68+
}
69+
}()
70+
}
71+
// Give them some time; then inspect the counts
72+
time.Sleep(time.Second)
73+
fmt.Println("readOperations final: ", atomic.LoadUint64(&readOperations))
74+
fmt.Println("writeOperations final: ", atomic.LoadUint64(&writeOperations))
75+
}
76+
77+
78+
// A shared read operation
79+
type ReadOperation struct {
80+
key int
81+
response chan int
82+
}
83+
84+
// A shared writer operation
85+
type WriteOperation struct {
86+
key int
87+
value int
88+
response chan bool
89+
}

0 commit comments

Comments
 (0)