A sync.Mutex lets only one goroutine run a section of code at a time. Lock and Unlock mark where that section starts and ends.
Lock blocks until no other goroutine holds the mutex, then claims it. Unlock releases it. Only the code between the two runs one goroutine at a time.
var mu sync.Mutex
counter := 0
mu.Lock()
counter++
mu.Unlock()
fmt.Println(counter) // 1defer mu.Unlock() placed right after Lock guarantees the mutex releases even if the goroutine returns early or panics. Without it, all 100 goroutines here would race on counter.
var mu sync.Mutex
var wg sync.WaitGroup
counter := 0
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
counter++
}()
}
wg.Wait()
fmt.Println(counter) // 100| Function | Description |
|---|---|
Lock() | Blocks until the mutex is free, then claims it. |
Unlock() | Releases a locked mutex. Panics if it isn't locked. |
TryLock() bool | Claims the mutex only if it's free right now; never blocks. |
RLock() | Claims a read lock. Any number of readers can hold it at once (RWMutex only). |
RUnlock() | Releases a read lock (RWMutex only). |
TryRLock() bool | Claims a read lock only if no writer holds it; never blocks (RWMutex only). |
RWMutex, and a copying gotcha
RWMutex lets any number of readers hold RLock at once, but Lock still excludes everyone. It pays off only when reads heavily outnumber writes.
type Cache struct {
mu sync.RWMutex
data map[string]int
}
func (c *Cache) Get(key string) int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data[key]
}
func (c *Cache) Set(key string, v int) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = v
}A sync.Mutex must never be copied after first use. Passing c Counter by value gives inc its own copy of mu, so the lock protects nothing. go vet flags this; the fix is a pointer receiver, func inc(c *Counter).
type Counter struct {
mu sync.Mutex
n int
}
func inc(c Counter) { // BUG: c is a copy
c.mu.Lock()
c.n++
c.mu.Unlock()
}100 goroutines increment the same counter, each holding the mutex for its single counter++. The result is always 100, never less.
$ go run mutexes.go
100