Go 语言笔记:协程
Go 协程
协程是 Go 语言实现的用户态线程,和线程的区别在于
- 线程创建和切换需要进入操作系统内核,进入内核必然会导致性能开销较大;协程是用户态线程,创建和切换都在用户代码中完成,无需进入操作系统内核,开销远远小于系统线程的创建很切换
- 线程在操作系统内核中创建时默认会分配一个较大的栈内存,绝大多数情况下,系统线程用不了这么多的内存,且栈内存空间分配后不能再有变化,可能会导致特殊场景下的栈内存溢出;协程默认栈大小只有2K,不够用时,协程的栈会自动扩大,也可以在过大的时候自动收缩,不会造成内存空间的浪费
协程是通过协作而不是抢占来进行切换,一个进程内部可以运行多个线程,每个线程又可以运行多个协程。线程负责对协程进行调度,同一个线程内部最多只有一个协程正在运行。
协程的状态
协程有以下三个态:就绪态、运行态、休眠态。
- 就绪态:具备运行能力,还没有得到运行机会
- 运行态:正在运行
- 休眠态:不具备运行能力,等待某些条件的触发
协程的使用
Go 语言创建协程很简单,只需要使用 go 关键字,就可以让一个方法协程化。下面示例中的 main 函数运行在一个特殊的协程上,称之为主协程,Add 方法使用 go 关键字启动了子协程,子协程和主协程并发执行,而 for 循环中的子协程也是并发执行。在调用 go 协程后往往不会等待任何返回值,直接忽略进而执行代码的下一行。如果主协程终止了,程序运行也就终止了,其他协程也不会继续运行,因此上述示例中我们在主协程中添加了休眠,可以去掉该行或者调整休眠参数来观察运行结果。
go
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
for i:=0; i<10; i++ {
go Add(i, i)
}
// 此处为了展示打印效果,让主协程休眠 10 秒,业务代码不要这么操作
time.Sleep(1 * time.Microsecond)
}
如果我们想要等待所有的 go 协程执行完之后再退出,我们如何知道协程是否执行完毕退出了呢? 这就要用到 Go 语言的通信模型了:消息机制(channel)