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


















