Golang / GoLang Concurrency Mastery Interview Questions
Summarise: channel vs mutex decision guide, and the top concurrency pitfalls.
This summary covers the most tested concurrency patterns and pitfalls in Go technical interviews.
| Scenario | Recommended Tool |
|---|---|
| Passing data ownership between goroutines | Channel |
| Signalling an event / broadcasting | Channel (close for broadcast) |
| Parallel pipeline of transformations | Directional channels |
| Limiting concurrency (semaphore) | Buffered channel or golang.org/x/sync/semaphore |
| Protecting a shared counter | sync/atomic or sync.Mutex |
| Protecting a shared map or struct | sync.Mutex or sync.RWMutex |
| One-time initialisation | sync.Once |
| Wait for a group of goroutines | sync.WaitGroup or errgroup |
| Read-heavy shared state | sync.RWMutex or sync.Map |
| Request cancellation / deadlines | context.Context |
| Pitfall | Symptom | Fix |
|---|---|---|
| Goroutine leak | NumGoroutine grows indefinitely | Pass context; close channels; use goleak |
| Data race | Inconsistent output; rare crashes | Run -race; mutex/atomic/channels |
| Deadlock | 'all goroutines asleep' | Consistent lock order; context timeouts |
| Nil interface error trap | if err != nil passes on nil *T | Return bare nil, not (*T)(nil) |
| Loop variable capture | All goroutines print same value | Pass as arg; use Go 1.22+ range |
| Mutex copied by value | Broken synchronisation silently | Always use *T containing mutex |
| time.After leak in loop | Goroutine/timer accumulation | Use NewTimer+Stop; prefer context |
| Send on closed channel | panic: send on closed channel | Only sender closes; done pattern |
| Lock held during I/O | High latency under concurrency | Release before I/O; re-acquire after |
| Missing defer wg.Done() | wg.Wait() never returns | Always defer wg.Done() in goroutine |
// Five essential concurrent patterns // 1. Worker with cancellation go func() { for { select { case <-ctx.Done(): return; case job := <-jobs: process(job) } } }() // 2. One-time safe initialisation var once sync.Once once.Do(func() { /* runs exactly once across all goroutines */ }) // 3. Concurrent tasks with error collection g, ctx := errgroup.WithContext(ctx) g.Go(func() error { return task1(ctx) }) g.Go(func() error { return task2(ctx) }) if err := g.Wait(); err != nil { handle(err) } // 4. Non-blocking channel operation select { case ch <- val: ; default: /* channel full */ } // 5. Timeout with automatic cleanup ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel()
Which tool is most appropriate for protecting a struct field read by 100 goroutines/sec and written once per minute?
What is the single most important flag to add to Go test runs for catching concurrency bugs early?
More Related questions...