什么是goroutine?
goroutine在go中作为协程存在,是go并发处理的主要手段。其并发程序主要基于CSP并发模型实现。
特点
- 有着轻量级的优势,初始化一个goroutine只有2KB的内存空间,使得上下文的切换代价非常小。
- 在一个goroutine中,每一个结构体G中存在一个sched的属性保存上下文,这样goroutine可以轻易来回切换。
- 上下文是在用户态下发生的,不必进入内核,只保存当前goroutine下的PC,SP等少量信息,所以速度很快。
如何使用?
goroutine的使用很简单,只需要使用go关键字即可启动,如下:
go func() {
fmt.Println("hello")
}()
如何调度实现的?
goroutine的调度是通过运行时(runtime)管理,采用GMP调度模型。
- G(goroutine):go协程,包含自己的栈、程序计数器等信息。
- M(machine):os内核线程,用于执行G。
- P(processor):处理器,一般保存当下goroutine运行的上下文环境,管理goroutine队列做调度。
- LRQ:本地运行队列。
- GRQ:全局运行队列。
调度过程:
- 某个线程尝试创建G对象,该G对象被安排到本地队列LRQ中,如果LRQ队列满了,则被分配到全局队列GRQ中。
- 尝试获取当前线程的M,如果没法获取,就会从空闲的M列表找一个出来,如果空闲列表也没有,就会创建一个M,然后绑定G到P运行。
- 执行G,完成后退出。
Go什么时候发生阻塞?阻塞时,调度器会怎么做。
- 用于原子、互斥量或通道操作导致goroutine阻塞,调度器将把当前阻塞的goroutine从本地运行队列LRQ换出,并重新调度其它goroutine;
- 由于网络请求和IO导致的阻塞,Go提供了网络轮询器(Netpoller)来处理,后台用epoll等技术实现IO多路复用。