> For the complete documentation index, see [llms.txt](https://ineelhere.gitbook.io/code-with-go/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ineelhere.gitbook.io/code-with-go/42-stateful-goroutines.md).

# Stateful Goroutines

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:

```go
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)
}
```

#### Output

```
readOps: 6723
writeOps: 680
```

Explanation:

1. `readOp` and `writeOp` structures represent read and write operations, respectively.
2. The main goroutine initializes channels for read and write operations, and a goroutine simulating a stateful object.
3. 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.
4. The `atomic` package is used to perform atomic operations on counters (`readOps` and `writeOps`) to track the number of read and write operations.
5. The program runs for some time (`time.Sleep(time.Second)`) to allow goroutines to perform operations.
6. 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.
