| name | go-sync-primitives |
| description | sync.WaitGroup and sync.Mutex patterns |
Sync Primitives
sync.WaitGroup - Wait for Goroutines
CORRECT
func processBatch(items []string) {
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1) // BEFORE launching goroutine
go func(item string) {
defer wg.Done()
process(item)
}(item)
}
wg.Wait() // Block until all done
}
WRONG - Add inside goroutine
func processBatch(items []string) {
var wg sync.WaitGroup
for _, item := range items {
go func(item string) {
wg.Add(1) // WRONG: race condition
defer wg.Done()
process(item)
}(item)
}
wg.Wait() // May return early
}
WRONG - Missing variable capture
func processBatch(items []string) {
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func() {
defer wg.Done()
process(item) // WRONG: captures loop variable
}()
}
wg.Wait()
}
sync.Mutex - Protect Shared State
CORRECT
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
WRONG - Unlocked access
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
c.value++ // What if panic happens?
c.mu.Unlock()
}
func (c *Counter) Value() int {
return c.value // WRONG: race condition
}
sync.RWMutex - Multiple Readers
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) (string, bool) {
c.mu.RLock() // Multiple readers OK
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}
func (c *Cache) Set(key, value string) {
c.mu.Lock() // Exclusive writer
defer c.mu.Unlock()
c.data[key] = value
}
Rules
WaitGroup
- Call
Add() before go statement
- Always use
defer wg.Done()
- Pass loop variables as function parameters
- One
Add(n) can count multiple goroutines
Mutex
- Always use
defer mu.Unlock()
- Keep critical sections small
- Don't hold locks during I/O or slow operations
- Use RWMutex for read-heavy workloads
- Never copy a mutex (pass by pointer)
sync.Once - Run Exactly Once
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
Race Detection
go test -race ./...
go run -race main.go