Go by Example: Mutexes

Go by Example : Mutexes

In the previous example we saw how to manage simple counter state using atomic operations . For more complex state we can use a mutex to safely access data across multiple goroutines.

package main
import (
    "fmt"
    "sync"
)

Container holds a map of counters; since we want to update it concurrently from multiple goroutines, we add a Mutex to synchronize access. Note that mutexes must not be copied, so if this struct is passed around, it should be done by pointer.

type Container struct {
    mu       sync.Mutex
    counters map[string]int
}

Lock the mutex before accessing counters ; unlock it at the end of the function using a defer statement.

func (c *Container) inc(name string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.counters[name]++
}

Note that the zero value of a mutex is usable as-is, so no initialization is required here.

func main() {
    c := Container{
        counters: map[string]int{"a": 0, "b": 0},
    }
    var wg sync.WaitGroup

This function increments a named counter in a loop.

    doIncrement := func(name string, n int) {
        for i := 0; i < n; i++ {
            c.inc(name)
        }
        wg.Done()
    }

Run several goroutines concurrently; note that they all access the same Container , and two of them access the same counter.

    wg.Add(3)
    go doIncrement("a", 10000)
    go doIncrement("a", 10000)
    go doIncrement("b", 10000)

Wait for the goroutines to finish

    wg.Wait()
    fmt.Println(c.counters)
}

Running the program shows that the counters updated as expected.

$ go run mutexes.go
map[a:20000 b:10000]

Next we’ll look at implementing this same state management task using only goroutines and channels.

Next example: Stateful Goroutines .