Thread pool in Go

Go has no OS thread pool in the standard library. You rarely need one: goroutines are cheap and the Go runtime already multiplexes them onto a small set of kernel threads (GOMAXPROCS).
To get the same functionality as a thread pool — limit concurrency and reuse workers — use a worker pool of goroutines reading jobs from a channel. See What is Thread Pool? and Goroutines.

1. Worker pool — N goroutines + jobs channel

Start a fixed number of worker goroutines. Producers send jobs on a channel; workers process and loop for the next job.


package main

import "fmt"

func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("worker %d processing job %d\n", id, job)
        results <- job * 2
    }
}

func main() {
    const numWorkers = 4
    jobs := make(chan int, 10)
    results := make(chan int, 10)

    // start fixed pool of workers
    for w := 0; w < numWorkers; w++ {
        go worker(w, jobs, results)
    }

    // submit 10 jobs
    for j := 1; j <= 10; j++ {
        jobs <- j
    }
    close(jobs)

    // collect results
    for r := 0; r < 10; r++ {
        fmt.Println("result:", <-results)
    }
}
      

2. Worker pool with sync.WaitGroup

Same idea, but main waits for all workers to finish before exiting.


package main

import (
    "fmt"
    "sync"
)

func main() {
    const numWorkers = 4
    jobs := make(chan int, 20)
    var wg sync.WaitGroup

    for w := 0; w < numWorkers; w++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for job := range jobs {
                fmt.Println("worker", id, "job", job)
            }
        }(w)
    }

    for j := 1; j <= 10; j++ {
        jobs <- j
    }
    close(jobs)
    wg.Wait()
    fmt.Println("all workers done")
}
      

3. Third-party goroutine pool — ants

When you want a reusable goroutine pool with a task API (closest to Java's ExecutorService), use a library such as github.com/panjf2000/ants/v2.


package main

import (
    "fmt"
    "sync"

    "github.com/panjf2000/ants/v2"
)

func main() {
    pool, _ := ants.NewPool(4) // max 4 concurrent goroutines
    defer pool.Release()

    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        n := i
        _ = pool.Submit(func() {
            defer wg.Done()
            fmt.Println("task", n)
        })
    }
    wg.Wait()
}
      

Summary: Go achieves thread-pool behavior with a bounded set of worker goroutines + channel queue. The runtime handles OS thread scheduling; you control application-level concurrency.