
go 程序中启动 goroutine 后立即退出 main 函数,会导致程序提前终止,新协程来不及执行——这是初学者最常见的并发陷阱。
在你提供的代码中,main 函数调用 go h.Myprint("need to go") 启动一个 goroutine,但该函数内部又启动了一个嵌套 goroutine 执行 fmt.Println。然而,main 函数本身在启动 goroutine 后立刻结束,整个进程随之退出,底层所有 goroutine(包括正在运行或待调度的)都会被强制终止,因此你完全看不到任何输出。
根本原因在于:Go 程序的生命周期由 main goroutine 决定;main 返回即程序退出,不等待其他 goroutine 完成。
✅ 正确做法:同步等待 goroutine 完成
最常用且推荐的方式是使用 sync.WaitGroup:
package main
import (
"fmt"
"sync"
)
type Hello struct {
a int
}
func (h *Hello) Myprint(value string, wg *sync.WaitGroup) {
defer wg.Done() // 标记当前 goroutine 完成
fmt.Println(value)
}
func main() {
h := &Hello{100}
var wg sync.WaitGroup
wg.Add(1)
go h.Myprint("need to go", &wg)
wg.Wait() // 阻塞 main,直到所有 Add 的 goroutine 调用 Done()
}✅ 输出:need to go
? 补充说明与注意事项
- 不要依赖 time.Sleep 做同步:虽然 time.Sleep(time.Second) 可能“碰巧”看到输出,但它不可靠、不精确,且掩盖了真正的并发控制问题。
- 方法接收者需注意所有权:本例中 Myprint 使用指针接收者(*Hello),这是合理的;若改用值接收者,虽能编译,但会复制结构体——对小对象影响小,但不符合并发场景下共享状态的常见意图。
- goroutine 泄漏风险:如果 Myprint 内部启动的 goroutine 因逻辑错误未执行 Done() 或陷入死锁,wg.Wait() 将永久阻塞。生产环境建议配合 context 设置超时。
-
更简洁的替代写法(无方法封装):
go func() { fmt.Println("need to go") }() time.Sleep(10 * time.Millisecond) // ❌ 不推荐,仅作演示
✅ 总结
Go 的并发模型强调“不要通过共享内存来通信,而应通过通信来共享内存”。但无论采用何种方式,显式同步是保障正确性的前提。sync.WaitGroup 是协调多个 goroutine 生命周期的基石工具,务必熟练掌握。记住:没有等待,就没有可见的并发效果。










