go并发基于goroutine轻量级模型,新手易错在误用go关键字、忽略同步及误判调度;channel是通信首选,需注意缓冲与关闭规则;waitgroup和context分别解决等待与取消问题;gosched和gomaxprocs通常无需手动干预。

Go 的并发不是靠多线程模拟出来的“假并发”,而是由 runtime 调度 goroutine 直接支持的轻量级并发模型;用错 go 关键字、忽略同步或误判调度行为,是新手最常翻车的三处。
goroutine 启动后不等于立即执行
写 go f() 只是向调度器提交一个任务,具体何时执行、是否执行完、是否被抢占,完全由 GMP 模型决定。常见误判是以为启动后立刻能读到结果:
- 没有同步机制(如
time.Sleep、sync.WaitGroup或channel)就退出 main 函数,所有 goroutine 会被强制终止 - 在循环中启动大量 goroutine 时,若未控制并发数,可能瞬间创建成千上万个,耗尽内存或触发 GC 压力
- goroutine 内部 panic 不会传播到 main,必须用
recover捕获,否则整个程序崩溃
示例:下面代码大概率打印空字符串或 panic
func main() {
var s string
go func() {
s = "done"
}()
fmt.Println(s) // 输出 "",因为 goroutine 还没执行完
}
channel 是 goroutine 间通信的首选,不是共享内存的替代品
Go 推崇 “不要通过共享内存来通信,而应通过通信来共享内存”。channel 天然带同步语义,比 sync.Mutex 更易写出正确逻辑:
本程序源码为asp与acc编写,并没有花哨的界面与繁琐的功能,维护简单方便,只要你有一些点点asp的基础,二次开发易如反掌。 1.功能包括产品,新闻,留言簿,招聘,下载,...是大部分中小型的企业建站的首选。本程序是免费开源,只为大家学习之用。如果用于商业,版权问题概不负责。1.采用asp+access更加适合中小企业的网站模式。 2.网站页面div+css兼容目前所有主流浏览器,ie6+,Ch
立即学习“go语言免费学习笔记(深入)”;
- 无缓冲 channel(
make(chan int))发送和接收必须配对阻塞,适合做信号同步 - 有缓冲 channel(
make(chan int, 10))可暂存数据,但缓冲区满时仍会阻塞发送,别误以为“不会阻塞” - 从已关闭的 channel 读取会立即返回零值;向已关闭的 channel 发送会 panic —— 关闭前务必确认无人再写
- 用
select配合default实现非阻塞收发,但要注意:如果多个 case 都就绪,select是随机选,不是按顺序
sync.WaitGroup 和 context.Context 解决不同层面的生命周期问题
sync.WaitGroup 管“等一组 goroutine 结束”,context.Context 管“通知一组 goroutine 该停了”:
-
WaitGroup.Add()必须在 goroutine 启动前调用,且不能在 goroutine 内部调用(竞态风险) -
context.WithCancel()返回的cancel函数应在合适时机显式调用,比如超时、错误或用户中断;不调用会导致 goroutine 泄漏 - goroutine 内部需监听
ctx.Done()并及时退出,尤其当它在阻塞 IO(如http.Get、time.Sleep)时,要传入带 timeout 的 context - 不要把
context.Context当作函数参数传递的“万能兜底”,只用于传递取消信号和请求范围的元数据(如 traceID)
runtime.Gosched() 和 GOMAXPROCS() 不是你该手动调的开关
绝大多数场景下,你不需要碰这两个:
-
runtime.Gosched()主动让出 P,仅在极少数 CPU 密集型循环中防止饿死其他 goroutine;滥用反而降低性能 -
GOMAXPROCS()默认等于 CPU 核心数,改大不会提升并发能力(P 数多了,M 调度开销上升),改小可能浪费资源;除非明确知道你在做 NUMA 绑核或调试调度行为,否则别动 - 真正影响并发效率的是 IO 阻塞点是否被 Go runtime 正确接管(比如用
net.Conn而非裸 socket),而不是手动干预调度器
goroutine 的本质是用户态协程,它的轻量(初始栈仅 2KB)和自动伸缩(栈可增长收缩)是优势,但这也意味着——你没法像线程那样靠“看进程线程数”判断负载,得用 runtime.NumGoroutine() 或 pprof 抓真实 goroutine 堆栈。泄漏往往藏在忘记关闭的 channel、未响应的 context 或死锁的 select 里。










