liveBook · Manning

Sync package

As explained in 100 Go Mistakes and How to Avoid Them

The sync package is a Go standard-library package providing synchronization primitives for coordinating concurrent execution and protecting shared state.

A mutex does not protect shared state if each goroutine locks a different copy of it. The Go standard library’s sync package provides mutexes, condition variables, wait groups, and other synchronization primitives. They coordinate concurrent work, but their behavior depends on sharing the same primitive. section 9.14

type Counter struct {
    mu       sync.Mutex
    counters map[string]int
}
 
func NewCounter() Counter {                    #1
    return Counter{counters: map[string]int{}}
}
 
func (c Counter) Increment(name string) {
    c.mu.Lock()                                #2
    defer c.mu.Unlock()
    c.counters[name]++
}

The value receiver on Increment copies Counter, including its sync.Mutex. Concurrent calls then lock separate mutexes while changing the same map. section 9.14

Do not copy a sync value after it is in use; this applies to sync.Cond, sync.Map, sync.Mutex, sync.RWMutex, sync.Once, sync.Pool, and sync.WaitGroup. For the counter, choose a fix that preserves a shared lock:

  • Use a pointer receiver, func (c *Counter) Increment(name string), so the method call does not copy Counter. section 9.14
  • If a value receiver is needed, store mu as *sync.Mutex and initialize it. An omitted pointer field is nil, so calling Lock on it panics. section 9.14

The package also helps when locking alone is not enough. Suppose one goroutine increments a donation balance while separate listeners wait for goals of $10 and $15. Repeatedly unlocking and relocking an RWMutex to check the balance creates a CPU-wasting busy loop. Sending each update on one shared channel is not a broadcast: only one listener receives each message, so the $10 listener might first observe $11. section 9.12

type Donation struct {
    cond    *sync.Cond                    #1
    balance int
}
 
donation := &Donation{
    cond: sync.NewCond(&sync.Mutex{}),    #2
}
 
// Listener goroutines
f := func(goal int) {
    donation.cond.L.Lock()
    for donation.balance < goal {
        donation.cond.Wait()              #3
    }
    fmt.Printf("%d$ goal reached\n", donation.balance)
    donation.cond.L.Unlock()
}
go f(10)
go f(15)
 
// Updater goroutine
for {
    time.Sleep(time.Second)
    donation.cond.L.Lock()
    donation.balance++                    #4
    donation.cond.L.Unlock()
    donation.cond.Broadcast()             #5
}

A sync.Cond pairs the balance with a lock. Each listener checks its goal in a loop around Wait; the updater changes the balance under the lock and calls Broadcast to notify the listeners. section 9.12

Use the shared condition variable when multiple listeners need to recheck their own conditions after an update. Use a shared channel with multiple receivers only when delivery to one receiver per message fits the task. When a lock appears ineffective, check for copied sync values: go test -race exposes the counter’s data race, and go vet reports that Increment passes a lock by value. section 9.12 section 9.14

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Works through a condition-variable implementation and a copied-mutex data race, then gives receiver and field-type fixes.
Examples
Contrasts donation-goal listeners using a busy loop, channel, and sync.Cond; tests a counter with a copied mutex.
What it assumes
Assumes familiarity with Go goroutines, channels, structs, and methods; demonstrates the synchronization mistakes through concrete code.
What you can do after
Diagnose/debug with it by recognizing copied locks and missed channel notifications, then choosing a shared lock or sync.Cond.
How it connects
Links mutexes and condition variables to goroutines, channels, shared state, data races, and value receivers.

Also explained in

Compare how 3 books explain sync package →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.