适合从限制使用场景起步,因goroutine是用户态协程而非线程封装,主goroutine退出会强制终止子goroutine,易导致资源耗尽、数据竞争等问题;应先掌握sync.WaitGroup、无缓冲channel、sync.Mutex三组基础组合。

适合,但必须从「限制使用场景」开始学,而不是一上来就写高并发服务。
为什么 goroutine 看似简单却容易翻车
新手常误以为 go func() 就是“开个线程”,结果写出一堆无法等待、无法取消、无法调试的 goroutine。根本原因在于:goroutine 不是线程封装,而是调度器管理的用户态协程——它不保证执行顺序,也不自动同步生命周期。
- 主 goroutine 退出时,所有子 goroutine 会立即被强制终止(哪怕它们还在
http.Get或time.Sleep) - 不加控制地启动 1000 个 goroutine 去请求 URL,可能瞬间打爆本地文件描述符或远端服务限流
- 用全局变量共享状态(比如
counter++)而不加sync.Mutex或原子操作,必现Data Race
新手该从哪几个 channel + sync 组合起步
别碰 select、context.WithCancel 或 worker pool 这类进阶模式。先掌握三组最小可行组合:
-
sync.WaitGroup+go:只用于「等所有任务结束」,例如并发读多个文件后合并结果 -
chan int(无缓冲)+for range:只用于「一个生产者 → 一个消费者」的简单结果传递,避免deadlock -
sync.Mutex+struct{ mu sync.Mutex; data map[string]int }:只在必须更新共享 map 或 slice 时加锁,且锁粒度要小(别锁整个函数)
示例中不要用 time.Sleep 模拟等待——那是伪并发,掩盖了真正的同步问题。
立即学习“go语言免费学习笔记(深入)”;
新手最常写的错误代码长什么样
以下代码看似合理,实则存在三个致命问题:
func main() {
for i := 0; i < 5; i++ {
go func() {
fmt.Println("task", i) // 闭包捕获变量 i,所有 goroutine 打印的都是 5
}()
}
// 没有 WaitGroup 或 channel 同步,main 直接退出
}
修正要点:
- 用
go func(id int)显式传参,避免闭包陷阱 - 加
var wg sync.WaitGroup,循环内wg.Add(1)和 goroutine 内defer wg.Done() - main 结尾必须
wg.Wait(),否则看不到输出
真正卡住新手的,从来不是语法,而是对「谁在什么时候停止」「数据从哪来又到哪去」缺乏明确预期。先写 5 行能稳定跑通的并发逻辑,再逐步加复杂度——比直接抄一个「高并发爬虫」模板有用十倍。











