gopool is a strand-based goroutine pool for Go.
It provides sequential execution per handle and concurrent execution across handles. If you schedule tasks through the same handle, they will never overlap. If you schedule tasks through different handles, they may run concurrently.
This makes gopool suitable for stateful, order-sensitive workloads
where you want concurrency without races.
A pool consists of a fixed number of strands. Each strand owns a task queue and runs tasks one at a time.
A Handle is a stable reference to a strand. Scheduling through the
same handle preserves order and exclusivity. Scheduling through
different handles allows concurrency.
The pool does not spawn goroutines per task. All goroutines are created upfront.
-
Tasks scheduled via the same handle:
- Execute sequentially
- Preserve submission order
- Never overlap
-
Tasks scheduled via different handles:
- May execute in parallel
- Have no ordering guarantees relative to each other
-
Panics inside tasks:
- Do not crash the pool
- Are optionally reported via a panic handler
-
Shutdown:
- Stops accepting new work
- Drains queued tasks
- Can be canceled via context
- Dynamic resizing
- Task prioritization
- Automatic retries or restarts
- Logging or opinionated observability
This pool is intentionally simple and explicit.
p := gopool.New(4, 16)
defer p.Shutdown(context.Background())
h := p.NewHandle()
_ = h.Schedule(context.Background(), func() {
// runs sequentially per handle
})p := gopool.New(
4,
16,
gopool.WithPanicHandler(func(strand int, value any, stack []byte) {
log.Printf("panic in strand %d: %v\n%s", strand, value, stack)
}),
)ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := p.Shutdown(ctx); err != nil {
// shutdown was canceled
}