Go Concurrency: Goroutines, Channels & Context

Goroutines vs OS threads, channels and select, WaitGroup and errgroup, mutex and sync.Map, context cancellation and timeouts — production Go concurrency.

AdvancedUpdated 2026-10-2886K views

Goroutines

A goroutine is a lightweight user-space thread managed by the Go runtime: initial stack is only 2KB, and the multiplexes M goroutines onto N OS threads. Creating a goroutine is nearly free — go f() — and millions can exist simultaneously.

go func(msg string) {
    fmt.Println(msg)
}("async hello")

// 注意:main 退出后所有 goroutine 立即终止
// 需要同步机制等待它们完成(见 WaitGroup)

Channels and select

// 无缓冲通道:同步握手
ch := make(chan int)

// 有缓冲通道:异步直到满
buf := make(chan int, 10)

buf <- 1            // 发送(缓冲满时阻塞)
v := <-buf          // 接收(缓冲空时阻塞)
close(buf)          // 关闭,之后接收立即得到零值

// range 遍历直到关闭
for n := range buf {
    fmt.Println(n)
}

// select:多路复用
select {
case v := <-ch:
    fmt.Println("got", v)
case <-time.After(2 * time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("no value ready")
}
Channels share memory by communicating; do not communicate by sharing memory. buffered channels are for throughput; unbuffered channels synchronize.

sync: WaitGroup, Mutex, errgroup

import "sync"

var (
    mu    sync.Mutex
    count int
)

func inc() {
    mu.Lock()
    count++
    mu.Unlock()
}

// WaitGroup:等待一组 goroutine
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        inc()
    }(i)
}
wg.Wait()

// errgroup:带错误传播的 WaitGroup(golang.org/x/sync)
g, ctx := errgroup.WithContext(context.Background())
for _, url := range urls {
    url := url
    g.Go(func() error { return fetch(ctx, url) })
}
if err := g.Wait(); err != nil { /* 第一个错误 */ }

Context: Cancellation and Timeouts

context.Context is the standard way to carry deadlines, cancellation signals and request-scoped values across API boundaries. Every blocking call in a server should accept a context.

func handler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
    defer cancel()

    select {
    case <-ctx.Done():
        w.WriteHeader(504)        // 超时/取消
        return
    case result := <-work(ctx):
        json.NewEncoder(w).Encode(result)
    }
}

// context.Background():根 context,main / 初始化用
// context.TODO():还没想好时占位
  • context.WithCancel / WithTimeout / WithValue derive from a parent; canceling a parent cancels all children.
  • Pass ctx as the first argument, never store it in a struct field.

Friends & Partners