Introduction
This lab aims to demonstrate how to use mutexes to safely access data across multiple goroutines.
This tutorial is from open-source community. Access the source code
This lab aims to demonstrate how to use mutexes to safely access data across multiple goroutines.
The problem to be solved in this lab is to increment a named counter in a loop using multiple goroutines, and ensure that the access to the counter is synchronized.
Container struct to hold a map of counters.Mutex to synchronize access to the counters map.Container struct should have an inc method that takes a name string and increments the corresponding counter in the counters map.inc method should lock the mutex before accessing the counters map, and unlock it at the end of the function using a defer statement.sync.WaitGroup struct to wait for the goroutines to finish.fmt.Println function to print the counters map.## Running the program shows that the counters
## updated as expected.
## Next we'll look at implementing this same state
## management task using only goroutines and channels.
There is the full code below:
// In the previous example we saw how to manage simple
// counter state using [atomic operations](atomic-counters).
// For more complex state we can use a [_mutex_](https://en.wikipedia.org/wiki/Mutual_exclusion)
// 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
}
func (c *Container) inc(name string) {
// Lock the mutex before accessing `counters`; unlock
// it at the end of the function using a [defer](defer)
// statement.
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func main() {
c := Container{
// Note that the zero value of a mutex is usable as-is, so no
// initialization is required here.
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)
}
In this lab, we learned how to use mutexes to safely access data across multiple goroutines. We created a Container struct to hold a map of counters, and used a Mutex to synchronize access to the counters map. We also implemented an inc method to increment the named counter, and used the sync.WaitGroup struct to wait for the goroutines to finish. Finally, we printed the counters map using the fmt.Println function.