go · level 3 · unit 4 · lesson 2
- meet it
- guess first
- your turn
- fix it
- nailed it
Tap a green word if you get lost.
meet it
A lets one goroutine at a time into the shared data.
A mutex, sync.Mutex, is a with one key. mu.Lock() takes the key, waiting if another goroutine has it, and mu.Unlock() hands it back. The code between them is a : only one goroutine can be inside at once, so count++ can no longer lose updates.
Write defer mu.Unlock() straight after Lock, so the key comes back however the function ends, even through a panic. Keep each critical section short, because everyone else queues while you hold the key.
Keep the mutex in a struct right beside the data it guards, and give the struct pointer-receiver methods. A copied mutex is a different lock guarding a different copy of the data.
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.Mutex
votes map[string]int
}
func (c *Counter) Vote(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.votes[name]++
}
func main() {
c := Counter{votes: map[string]int{}}
var wg sync.WaitGroup
for _, v := range []string{"cats", "dogs", "cats", "cats", "dogs"} {
wg.Add(1)
go func(v string) { defer wg.Done(); c.Vote(v) }(v)
}
wg.Wait()
fmt.Println(c.votes)
} Without the mutex, two goroutines writing one map at once can crash the program outright.
guess first
A hundred goroutines each add ten. What is n?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
var wg sync.WaitGroup
n := 0
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
mu.Lock()
n++
mu.Unlock()
}
}()
}
wg.Wait()
fmt.Println(n)
}
your turn
Your turn. Guard the coin jar.
Fill in the method that takes the lock and the one that hands it back. Then choose how many coins each of the 50 goroutines drops in.
output
press run to see what happens
fix it
The shelf stays empty. Can you fix it?
output
press run to see what happens
- The shelf ends with 0 items. Whose
itemsdoesRestockchange? - With
(s Stock)every call works on a copy: a copy of the items and a copy of the lock. - Change the receiver to
(s *Stock), so every call shares the one real shelf.
nailed it
one goroutine at a time, please. :D
LockandUnlockwrap a short critical section.defer mu.Unlock()hands the key back however you leave.- Pointer receivers always: a copied mutex guards nothing.
new words in your collection
- mutex
- lock
- critical section
This lesson isn’t saved. A free account remembers every lesson you finish.