Backend Development 10 min read

Controlling Goroutine Concurrency in Go: Risks of Unbounded Goroutine Creation and Practical Limiting Techniques

The article explains why creating unlimited Goroutines in Go can exhaust system resources, demonstrates the problem with a sample program, and presents practical methods—including channel throttling, sync.WaitGroup, and third‑party Goroutine pools—to safely limit concurrency and improve performance.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Controlling Goroutine Concurrency in Go: Risks of Unbounded Goroutine Creation and Practical Limiting Techniques

Author: Yangyang, senior security R&D engineer at Ctrip, proficient in Python and Go, responsible for security tool development.

Many Go learners assume that because Goroutines are lightweight they can spawn millions without concern, but uncontrolled creation can crash the service.

Problem demonstration: The following program creates an unbounded number of Goroutines, quickly exhausting CPU and memory until the OS kills the process.

func main() {
    number := math.MaxInt64
    for i := 0; i < number; i++ {
        go func(i int) {
            // business logic
            fmt.Printf("go func: %d\n", i)
            time.Sleep(time.Second)
        }(i)
    }
}

Running this without limits causes high CPU/memory usage, as shown by the top command screenshots in the original article.

Basic concepts: A process is an OS‑level execution unit; a thread is a lightweight process sharing the same address space; a coroutine (or micro‑thread) is a cooperative task that shares heap but not stack. Goroutine is Go’s implementation of a coroutine that can run on one or more OS threads.

Creating a Goroutine is as simple as prefixing a function call with go :

func number() {
    for i := 0; ; i++ {
        fmt.Printf("%d ", i)
    }
}

func main() {
    go number() // start a Goroutine
    number()
}

Goroutine pool discussion: Some developers suggest using a pool to limit concurrency, but the original Zhihu answer argues that Goroutine pools contradict the design goal of Goroutines. In most cases they are unnecessary because Goroutine creation is cheap (≈2 KB). Only in extreme performance‑critical scenarios might a pool (e.g., ants ) be useful.

Limiting concurrency with channels and sync: Go provides channel for safe communication between Goroutines and sync.WaitGroup for coordination. The example below limits the number of concurrent Goroutines using a custom limiter based on a buffered channel and a WaitGroup.

package main

import (
    "fmt"
    "sync"
    "time"
)

type Glimit struct {
    n int
    c chan struct{}
}

// initialization Glimit struct
func New(n int) *Glimit {
    return &Glimit{
        n: n,
        c: make(chan struct{}, n),
    }
}

// Run f in a new goroutine but with limit.
func (g *Glimit) Run(f func()) {
    g.c <- struct{}{}
    go func() {
        f()
        <-g.c
    }()
}

var wg = sync.WaitGroup{}

func main() {
    number := 10
    g := New(2)
    for i := 0; i < number; i++ {
        wg.Add(1)
        value := i
        goFunc := func() {
            // business logic
            fmt.Printf("go func: %d\n", value)
            time.Sleep(time.Second)
            wg.Done()
        }
        g.Run(goFunc)
    }
    wg.Wait()
}

Conclusion: Uncontrolled Goroutine creation can exhaust system resources; developers should choose the appropriate limiting strategy—simple channel throttling, sync.WaitGroup coordination, or a mature library like ants —based on their specific scenario.

Thought questions:

Why use sync.WaitGroup ?

Why define the channel element as an empty struct instead of bool ?

Answers: WaitGroup ensures the main program waits for all Goroutines to finish; an empty struct occupies zero bytes, saving memory compared to a bool which uses one byte.

performanceconcurrencyGogoroutinesyncchannel
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.