go程序在main函数返回后立即终止,不会等待其他goroutine完成;main结束即强制回收所有非主goroutine,导致输出丢失;time.sleep并非同步机制,无法保证goroutine执行完毕。

Go 程序在 main 函数返回后立即终止,**不会等待任何其他 goroutine 完成**——这是所有 goroutine 看似“没执行”或“输出丢失”的根本原因。
为什么 goroutine 会突然消失?
Go 的程序生命周期完全由 main 函数控制。只要 main 执行完最后一行(包括 defer、return 或函数末尾隐式 return),运行时就回收全部资源,强制杀死所有仍在运行的非主 goroutine。
- 哪怕那个 goroutine 已经开始执行
fmt.Println,只要系统调用还没完成或缓冲区还没刷出,输出就会丢 -
time.Sleep不是同步机制:它只让main暂停,但无法保证 goroutine 已执行完毕;若 sleep 时间不够,照样丢输出 - 向无缓冲 channel 发送(如
c )仅阻塞到接收端开始执行 <code>,不保证后续语句执行完成
用 sync.WaitGroup 等待 goroutine 结束
这是最常用、语义清晰且适合多数场景的同步方式。核心是显式声明“我要等几个 goroutine”,并在它们结束时通知。
- 在
main中调用wg.Add(n),n 是要等的 goroutine 数量 - 每个 goroutine 内部结尾处必须调用
wg.Done()(推荐用defer wg.Done()防漏) -
main最后调用wg.Wait(),该调用会阻塞直到计数器归零 - 注意:
WaitGroup实例不能复制,需传指针;Add必须在go启动前调用
package main
<p>import (
"fmt"
"sync"
)</p><p>func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("worker %d done\n", id)
}</p><p>func main() {
var wg sync.WaitGroup
wg.Add(2)
go worker(1, &wg)
go worker(2, &wg)
wg.Wait() // 主 goroutine 在这里阻塞
}</p>
用 channel 实现 goroutine 完成信号
适合需要传递结果或精确控制执行顺序的场景。channel 天然具备同步语义,且能配合 select 做超时处理。
- 创建一个无缓冲 channel(如
done := make(chan bool)),或带缓冲的make(chan struct{}, 1) - goroutine 完成工作后发送信号:
done -
main通过接收,该操作会阻塞直到信号发出 - 不要用
time.After替代:时间不可靠,且违背了 Go 的“不要通过共享内存通信”原则
容易被忽略的关键点
真正难的不是写对语法,而是判断“哪里才算真正完成”。比如:
- 如果 goroutine 写日志到文件,
os.File.Write返回 ≠ 数据已落盘,需考虑file.Sync() - 如果 goroutine 发起 HTTP 请求,
resp.Body.Close()后才算彻底释放资源 - 如果 goroutine 启动了子进程,需确保
cmd.Wait()调用完成,否则进程可能残留 - WaitGroup 和 channel 都不能解决死锁:比如 goroutine 忘记发信号,或
main在 goroutine 启动前就调用了Wait
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











