Concurrency Design Guidelines
Overview
Concurrency can introduce subtle bugs. Follow these guidelines to write safe concurrent code.
Avoid Goroutine Leaks
// Bad: goroutine leaks if nobody receives
go func() {
ch <- result // Blocks forever
}()
// Good: use context cancellation
go func() {
select {
case ch <- result:
case <-ctx.Done():
}
}()Always Close Channels from Sender
// Producer closes
func producer(out chan<- int) {
for i := 0; i < 10; i++ {
out <- i
}
close(out) // Only sender closes
}
// Consumer ranges
for v := range in {
process(v)
}Race Detection
go test -race ./...
go run -race main.goCommon Patterns
Worker Pool
jobs := make(chan Job)
for i := 0; i < workers; i++ {
go worker(jobs)
}Bounded Concurrency
sem := make(chan struct{}, maxConcurrent)
for _, task := range tasks {
sem <- struct{}{}
go func(t Task) {
defer func() { <-sem }()
process(t)
}(task)
}Deadlock Prevention
// Deadlock: circular wait
ch := make(chan int)
ch <- 1 // Blocks: no receiver
<-ch // Never reached
// Fix: buffer or separate goroutine
ch := make(chan int, 1)
ch <- 1
<-chSummary
| Guideline | Reason |
|---|---|
| Share by communicating | Clearer ownership |
| Use context for cancellation | Prevent leaks |
| Only sender closes | Avoid panic |
| Run race detector | Catch data races |