This Go code demonstrates concurrent read and write operations on a state map using goroutines and channels. The program tracks the number of read and write operations using atomic
operations. Let's break down the code with inline comments:
package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
// readOp represents a read operation.
type readOp struct {
key int
resp chan int
}
// writeOp represents a write operation.
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
// Variables to track the number of read and write operations using atomic counters.
var readOps uint64
var writeOps uint64
// Channels for communication between goroutines.
reads := make(chan readOp)
writes := make(chan writeOp)
// Goroutine simulating a stateful object with read and write operations.
go func() {
// Initial state map.
var state = make(map[int]int)
for {
select {
case read := <-reads:
// Handling read operation: responding with the value from the state map.
read.resp <- state[read.key]
case write := <-writes:
// Handling write operation: updating the state map with the provided key-value pair.
state[write.key] = write.val
write.resp <- true
}
}
}()
// Goroutines simulating concurrent read operations.
for r := 0; r < 100; r++ {
go func() {
for {
// Generating a random key for the read operation.
read := readOp{
key: rand.Intn(5),
resp: make(chan int),
}
reads <- read
<-read.resp
// Incrementing the readOps counter using atomic operation.
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
// Goroutines simulating concurrent write operations.
for w := 0; w < 10; w++ {
go func() {
for {
// Generating random key-value pair for the write operation.
write := writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool),
}
writes <- write
<-write.resp
// Incrementing the writeOps counter using atomic operation.
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
// Allowing some time for goroutines to perform read and write operations.
time.Sleep(time.Second)
// Loading and printing the final values of readOps and writeOps counters.
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
readOps: 6723
writeOps: 680
Explanation:
-
readOp
andwriteOp
structures represent read and write operations, respectively. -
The main goroutine initializes channels for read and write operations, and a goroutine simulating a stateful object.
-
Concurrent goroutines are created to simulate both read and write operations. The read operations retrieve a value from the state, and the write operations update the state.
-
The
atomic
package is used to perform atomic operations on counters (readOps
andwriteOps
) to track the number of read and write operations. -
The program runs for some time (
time.Sleep(time.Second)
) to allow goroutines to perform operations. -
The final values of the readOps and writeOps counters are loaded atomically and printed.
The code showcases a scenario where multiple goroutines concurrently read and write to a shared state, and atomic
operations are used to safely track the number of operations. The use of channels ensures proper synchronization between goroutines.