在 Go 中,协程(goroutine)的生命周期大致可以分为以下几个阶段:
1. 创建阶段
协程的创建通常是通过 go
关键字来启动的。例如:
go func() {// 协程执行的代码
}()
当执行 go func()
时,Go 会在后台启动一个新的协程,新的协程会并发执行传入的函数体。协程是由 Go 的调度器来管理的,调度器负责在多个协程之间进行切换。
2. 运行阶段
协程创建后会进入运行阶段,它会由 Go 的调度器分配一个操作系统线程来执行。协程的执行并不一定是立即开始的,具体的执行顺序由调度器控制。调度器会在所有正在执行的协程之间公平地分配 CPU 时间片。
3. 阻塞阶段
协程可能会在运行时进入阻塞状态,常见的阻塞情况有:
- 等待 I/O 操作(例如文件读取、网络请求等)
- 等待锁(例如
sync.Mutex
) - 等待通道操作(例如
chan
的发送或接收)
当协程遇到这些阻塞情况时,Go 的调度器会把该协程挂起,并分配 CPU 时间给其他可以运行的协程。
4. 结束阶段
协程的执行将一直持续到它所执行的函数或代码块完成。当一个协程的代码执行完毕时,它会结束。如果该协程有返回值,通常会将结果传递到一个通道(chan
)中,供其他协程或主程序使用。
5. 垃圾回收阶段
协程结束后,如果没有其他引用指向该协程的相关资源,Go 的垃圾回收机制(GC)将会清理掉协程使用的内存和资源。这意味着如果协程持有某些资源(如文件句柄、网络连接等),需要手动关闭或释放这些资源,通常可以通过 defer
来确保。
6. 等待协程结束
在主程序或其他协程中,我们可以通过 sync.WaitGroup
来等待协程完成。例如:
var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1) // 增加一个等待任务go func(i int) {defer wg.Done() // 完成时调用 Donefmt.Println(i)}(i)
}wg.Wait() // 等待所有协程完成
协程的生命周期总结
- 创建:使用
go
关键字启动协程。 - 运行:协程被调度器分配线程执行代码。
- 阻塞:协程在等待某些操作时可能进入阻塞状态。
- 结束:协程执行完毕,生命周期结束。
- 垃圾回收:协程结束后,相关资源被清理。
Go 语言的协程非常轻量级,可以同时运行成千上万的协程,这是 Go 在处理并发任务时的一个优势。