提示:本网站现在为维护调试状态

Go 语言并发:goroutine + channel + sync.WaitGroup

Choyeon
Choyeon
2026年9月8日 1 分钟阅读 590 阅读
559 字 1,909 字符 更新于 2026年11月8日
Go 语言并发:goroutine + channel + sync.WaitGroup
目录

Go 最引以为豪的特性就是原生并发模型:"不要通过共享内存通信,而要通过通信共享内存"。goroutine + channel 的 CSP 模式让并发代码可读性和安全性大幅提升。

goroutine 与工作池模式

一个 goroutine 初始栈仅 2KB,可以轻松启动十万级别。通过带缓冲 channel 实现固定大小 worker pool,避免无限创建导致调度压力,用 sync.WaitGroup 等待所有 worker 退出。

package main

import (
    "context"
    "fmt"
    "log"
    "sync"
    "time"
)

type Job struct{ ID int; URL string }
type Result struct{ JobID int; Latency time.Duration; Status int; Err error }

func worker(ctx context.Context, id int, jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) {
    defer wg.Done()
    log.Printf("[worker %d] started", id)
    for {
        select {
        case <-ctx.Done():
            log.Printf("[worker %d] cancelled: %v", id, ctx.Err())
            return
        case job, ok := <-jobs:
            if !ok { return }
            t0 := time.Now()
            select {
            case <-ctx.Done():
                results <- Result{JobID: job.ID, Err: ctx.Err()}
                return
            case <-time.After(500 * time.Millisecond):
                results <- Result{JobID: job.ID, Latency: time.Since(t0), Status: 200}
            }
        }
    }
}

func main() {
    const (
        workerCount = 4
        jobCount    = 20
    )
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    jobs := make(chan Job, workerCount*2)
    results := make(chan Result, workerCount*2)
    var wg sync.WaitGroup
    for i := 1; i <= workerCount; i++ {
        wg.Add(1)
        go worker(ctx, i, jobs, results, &wg)
    }
    go func() {
        for i := 1; i <= jobCount; i++ {
            jobs <- Job{ID: i, URL: fmt.Sprintf("https://api.example.com/item/%d", i)}
        }
        close(jobs)
    }()
    go func() { wg.Wait(); close(results) }()
    total := 0
    for r := range results {
        total++
        if r.Err != nil { fmt.Printf("[job %02d] ERROR %v\n", r.JobID, r.Err) }
        fmt.Printf("[job %02d] status=%d latency=%-8s total=%d\n", r.JobID, r.Status, r.Latency, total)
    }
    fmt.Printf("Done: %d/%d jobs\n", total, jobCount)
}

Context 取消与陷阱

context.WithTimeout/WithCancel 同时关闭所有嵌套协程,是防止泄漏的关键。常见陷阱:for-range 读已关闭 channel 自动退出、select 加 default 变成忙循环、无缓冲 channel 发送端阻塞造成 goroutine 悬挂。

特性 Go goroutine+channel Java Thread+Queue Rust async+tokio
最小开销 ~2KB栈 ~1MB栈 结构体级
启动10万耗时 <100ms 数秒/OOM ~200ms
同步原语 channel/mutex/Cond BlockingQueue/锁 tokio::sync::*
取消机制 Context统一 Future.cancel/中断 Drop/select!
常见陷阱 goroutine泄漏/阻塞 虚假唤醒/死锁 Pin! 生命周期

最佳实践

发送端 close channel,不要接收端关。优先使用有界缓冲 channel,避免无界队列导致内存暴涨。用 defer 覆盖所有关闭路径,防止异常下 goroutine 泄漏。

X微博Telegram

本文作者

投票

你最希望 Rosetta 优先加入哪项功能?

社区投票(演示数据,可后台删除)

评论 (6)

0 / 3000
摸鱼达人小王
摸鱼达人小王2026年9月21日

好文!但是我觉得第三部分那个消息队列选型对比表有点偏向 Kafka 了,RocketMQ 在事务消息这块其实优势挺明显的。

机器学习萌新
机器学习萌新2026年9月21日

受益匪浅,感谢大佬分享!

运维老司机
运维老司机ops-notes.cn2026年9月21日

哈哈,楼主的踩坑经历和我一模一样!我当时就是没注意到这个配置,排查了整整两天,看到这篇文章泪目了。

数据库小管家
数据库小管家dbtips.pro2026年9月20日

楼主,我按照你的步骤操作后启动报错:Error creating bean with name 'dataSource',版本是 Spring Boot 3.2,请问有遇到过吗?

架构师小李
架构师小李archinsights.blog2026年9月20日

代码示例里那个 SQL 查询是不是应该加个 LIMIT?不然数据量大的时候全表扫描会很慢哦。

全栈打工人
全栈打工人2026年9月20日

写得太好了,终于搞懂了分布式事务的几种方案区别,收藏了!

相关文章

Redis 缓存设计模式

Redis 缓存设计模式

系统讲解三种核心缓存模式:Cache-Aside(旁路缓存)一致性保障、Write-Through(写穿透)原子性保证、Write-Behind(写回)高吞吐异步写入,以及缓存击穿/穿透/雪崩解决方案。

1 分钟阅读
Node.js 性能诊断:clinic.js + flamegraph

Node.js 性能诊断:clinic.js + flamegraph

系统学习 Node.js 性能定位工具链:clinic.js 一键生成分析报告,解读火焰图热点函数、阻塞事件循环的同步代码、内存泄漏对象栈,精准定位 CPU/内存瓶颈。

1 分钟阅读
PostgreSQL 查询优化技巧

PostgreSQL 查询优化技巧

深入掌握 PostgreSQL 查询优化的核心方法:从 EXPLAIN ANALYZE 执行计划解读,到索引类型选择(B-tree/GIN/BRIN)、统计信息更新、连接策略调整、分区表设计,全方位提升数据库查询性能。

1 分钟阅读
Rust 所有权与借用机制:彻底理解生命周期

Rust 所有权与借用机制:彻底理解生命周期

系统攻克 Rust 最独特概念:所有权规则、可变/不可变借用约束、引用生命周期标注。通过编译错误示例 + 修复方案,彻底掌握借用检查器思维方式,写出安全零抽象开销的系统代码。

1 分钟阅读
Linux 性能调优:perf_events + bpftrace + BCC

Linux 性能调优:perf_events + bpftrace + BCC

从 CPU、内存、IO、网络四个维度,使用 perf_events 采样堆栈、bpftrace 一行脚本追踪内核点、BCC 工具集 biolatency/offcputime,快速定位生产服务器性能瓶颈,附真实排查思路。

1 分钟阅读
Python 并发:asyncio vs ThreadPool vs ProcessPool

Python 并发:asyncio vs ThreadPool vs ProcessPool

深度对比 Python 三大并发模型:GIL 限制下的线程池(IO 密集)、进程池(绕 GIL 适合 CPU 密集)、asyncio 协程(超高并发 IO)。从原理/性能/复杂度四维度给出选型决策树。

1 分钟阅读