Go goroutine基本概念与创建
Goroutine是Go并发编程的基础,轻量高效。
什么是Goroutine
定义
Goroutine是Go运行时管理的轻量级线程,由Go调度器调度执行。
Go
// 创建goroutine
go func() {
fmt.Println("Hello from goroutine")
}()
特性
| 特性 | Goroutine | OS线程 |
|---|---|---|
| 初始栈大小 | 2KB | 1-8MB |
| 最大栈限制 | 1GB | 固定 |
| 创建开销 | 极低 | 较高 |
| 切换开销 | 低 | 高 |
| 数量限制 | 百万级 | 数千 |
创建Goroutine
go关键字
Go
// 函数形式
go myFunction()
// 匿名函数
go func() {
fmt.Println("匿名函数")
}()
// 匿名函数传参
go func(name string) {
fmt.Println("Hello", name)
}("Tom")
立即执行
Go
func main() {
go fmt.Println("并发") // 立即启动
fmt.Println("主线程") // 可能先或后输出
time.Sleep(100 * time.Millisecond) // 等待goroutine
}
go语句立即返回,不等待goroutine完成。
Goroutine生命周期
创建到结束
Go
go func() {
// 1. 创建:状态Runnable
// 2. 调度执行:状态Running
// 3. 执行完毕:状态Dead
}()
等待完成
Go
// 使用WaitGroup等待
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("工作")
}()
wg.Wait() // 等待完成
Goroutine与主程序
主程序退出时goroutine终止
Go
func main() {
go func() {
time.Sleep(1 * time.Second)
fmt.Println("可能不打印") // main已退出
}()
// main立即退出,goroutine被终止
}
必须等待
Go
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(1 * time.Second)
fmt.Println("会打印")
}()
wg.Wait() // 等待goroutine
}
Goroutine栈增长
动态扩容
Go
// 栈从2KB开始
// 函数调用深度增加时自动扩容
func deep(n int) {
if n <= 0 {
return
}
var buf [1024]byte // 使用栈空间
deep(n - 1) // 可能触发扩容
}
go deep(10000) // 栈可能增长到数MB
栈收缩
Go
// GC时检查栈使用率
// 使用率低时自动收缩
// 最终回到最小2KB
Goroutine数量
获取数量
Go
// 返回当前存在的goroutine数
n := runtime.NumGoroutine()
fmt.Println("Goroutine数量:", n)
大量goroutine
Go
// Go可以轻松创建百万goroutine
for i := 0; i < 1000000; i++ {
go func() {
time.Sleep(1 * time.Second)
}()
}
// 内存占用约2GB(2KB × 100万)
// 比百万OS线程(1MB × 100万 = 1TB)高效得多
常见陷阱
闭包捕获变量
Go
// 错误:所有goroutine打印相同值
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i) // 可能全打印10
}()
}
// 正确:传递参数
for i := 0; i < 10; i++ {
go func(n int) {
fmt.Println(n) // 打印0-9
}(i)
}
goroutine泄漏
Go
// 错误:goroutine永久阻塞
func leak() {
ch := make(chan int)
go func() {
<-ch // 永久等待,goroutine泄漏
}()
// ch从未发送数据
}
// 正确:确保goroutine能退出
func noLeak() {
ch := make(chan int, 1)
go func() {
v, ok := <-ch
if !ok {
return // channel关闭时退出
}
}()
ch <- 1
close(ch)
}
Goroutine vs Thread
| 对比项 | Goroutine | Thread |
|---|---|---|
| 内存开销 | 2KB起 | 1MB起 |
| 创建耗时 | 微秒 | 毫秒 |
| 切换耗时 | 纳秒 | 微秒 |
| 调度方式 | Go调度器 | OS调度 |
| 最大数量 | 百万 | 数千 |
| 栈伸缩 | 动态 | 固定 |
要点总结
- Goroutine是轻量级线程,由Go调度器管理
- 初始栈2KB,可动态增长到1GB
- go关键字立即启动goroutine
- 主程序退出时goroutine终止
- 用WaitGroup等待goroutine完成
- 闭包捕获变量是常见陷阱
- 避免goroutine泄漏
- 可轻松创建百万goroutine
- 比OS线程高效得多
📝 发现内容有误?点击此处直接编辑