
go程序在main函数执行完毕后立即终止,不会等待其他goroutine完成;即使goroutine已启动并阻塞在channel接收上,若main函数提前退出,其打印语句将永远不会执行。
go程序在main函数执行完毕后立即终止,不会等待其他goroutine完成;即使goroutine已启动并阻塞在channel接收上,若main函数提前退出,其打印语句将永远不会执行。
在你提供的示例中,看似两个 goroutine 都已启动并准备就绪,但实际运行结果往往只输出:
Start 1 End
而 程序过早退出。
执行流程剖析
- main 启动两个 goroutine:一个监听 start,一个监听 end;
- 两个 goroutine 立即打印 "Start" 和 "End"(顺序不保证,但通常可见);
- main 执行 start
- main 紧接着执行 end
- 此时 goroutine 已获取到 2,正准备执行 fmt.Println(后续语句(即 fmt.Println(2));
- 但 main 函数已无其他语句,立即退出 → 整个进程终止,该 Println 被强行中断,永不执行。
✅ 关键点:
正确做法:确保 main 等待 goroutine 完成
推荐使用 sync.WaitGroup(如你所知),也可配合 time.Sleep(仅用于演示,不可用于生产):
package main
import (
"fmt"
"sync"
)
func main() {
start := make(chan int)
end := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println("Start")
fmt.Println(<p>✅ 输出确定为:</p><pre class="brush:php;toolbar:false;">Start
1
End
2其他注意事项
- ❌ 不要依赖 time.Sleep(1 * time.Second) 来“凑巧”等待——竞态仍存在,且违背确定性原则;
- ✅ Channel 发送/接收本身是同步的,但goroutine 的生命周期由调度器管理,不与 main 绑定;
- ? 若需更复杂的协作(如取消、超时),可结合 context.Context;
- ⚠️ defer wg.Done() 必须在 goroutine 内部调用,确保无论何种路径退出都计数归零。
总之,Go 的并发模型要求开发者显式管理生命周期。main 函数不是“主线程”的父容器,而是整个程序的退出守门员——它一结束,一切灰飞烟灭。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











