Skip to content

Go Stateful Goroutines

In Go, you can manage shared state by using stateful goroutines. Instead of using a Mutex to lock a variable, you "own" the state inside one goroutine and communicate with it using channels.

This follows the Go mantra: "Do not communicate by sharing memory; instead, share memory by communicating."

1. The Core Pattern

A stateful goroutine consists of: 1. A private variable (the state). 2. A channel to receive read/write requests. 3. A loop that processes requests one by one.

package main

import (
    "fmt"
    "math/rand"
    "sync/atomic"
    "time"
)

// readOp and writeOp represent requests to the stateful goroutine
type readOp struct {
    key  int
    resp chan int
}
type writeOp struct {
    key  int
    val  int
    resp chan bool
}

func main() {
    // These channels will be used to issue requests
    reads := make(chan readOp)
    writes := make(chan writeOp)

    // This goroutine owns the state
    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
            }
        }
    }()

    // Now other goroutines can safely "read" and "write" via channels
    // without ever using a Mutex!
}

2. Benefits of Stateful Goroutines

  1. No Race Conditions: Since only one goroutine ever touches the state map, it's impossible to have a data race.
  2. Sequential Consistency: Operations are processed in the order they arrive in the channel.
  3. Encapsulation: The logic for how state is updated is hidden inside the goroutine.

3. Comparison with Mutexes

Feature Mutex Stateful Goroutine
Complexity Simple for small state. Better for complex logic.
Performance Usually faster (less overhead). Slower due to channel context switching.
Safety Requires careful Lock/Unlock. Inherently safe by design.

When to use this?

Use stateful goroutines when: - The state management logic is complex (e.g., a chat room server where users join/leave). - You want to avoid the mental overhead of tracking multiple Mutexes. - The performance overhead of channels is acceptable for your use case.