Worker Pools
TL;DR
A Worker Pool is a concurrency pattern used to limit the number of active goroutines. Instead of spawning 1,000,000 goroutines to process 1,000,000 jobs (which might exhaust your memory or crash a database with too many connections), you spawn a fixed number of “Workers” (e.g., 100) that constantly pull jobs from a shared queue (Channel).
Mental Model
How It Works
- Create a
jobschannel and aresultschannel. - Spawn a fixed number
Nof worker goroutines. Each worker runs afor job := range jobsloop. - The main thread iterates over all tasks, pushing them into the
jobschannel. - Because the workers are reading from the channel concurrently, Go automatically distributes the jobs among the available workers.
- Close the
jobschannel when all tasks are dispatched. The workers will automatically exit their loops when the channel is empty.
Example
package main
import (
"fmt"
"time"
)
// The Worker function
func worker(id int, jobs <-chan int, results chan<- int) {
// This loop runs until the 'jobs' channel is closed!
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
time.Sleep(time.Second) // Simulate heavy CPU work
results <- j * 2
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
// 1. Start exactly 3 workers
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 2. Send 5 jobs into the channel
for j := 1; j <= numJobs; j++ {
jobs <- j
}
// 3. Close the channel so workers know no more jobs are coming
close(jobs)
// 4. Collect all the results
for a := 1; a <= numJobs; a++ {
<-results
}
}
Common Interview Questions
If Goroutines are so cheap, why do we need Worker Pools?
While it’s true you can easily spawn 100,000 goroutines, the systems those goroutines interact with often cannot handle that concurrency. If every goroutine opens a file, you will hit the OS ulimit (Too many open files). If every goroutine hits an external API, you will get rate-limited. Worker pools act as a throttle to protect downstream services.
How is this different from a WaitGroup?
A WaitGroup just waits for a dynamic number of tasks to finish, but provides no cap on how many run simultaneously. You often use a WaitGroup inside a Worker Pool to wait for the workers themselves to finish exiting!