协程的概念其实不好定义,每种语言都有自己的实现方式,比如go语言的协程实现需要依赖整套的GMP模型,协程的任务内容封装在G里面,G需要存在P的队列里面,M则负责从P的队列里面取下G去执行;其中有一个很重要的区别于其他G的协程任务,G0!他会检测M执行状态,当M执行G进入阻塞状态,导致当前P中其他任务无法执行时,则会给这个P安排一个新的线程M去执行,原来的M阻塞完后则服务于其他的P或者进入休眠状态;所以go语言初始化时P的数量是固定的,M的数量是动态变化的。
在GMP模型中除了跟P绑定的线程外还有一个很重要的线程:主线程,就是这个主线程负责初始化和启动整套系统,初始玩整套系统之后就会跟其他的M线程一样服务于其他的P。
M其实是一个线程对象(linux内核表现为一个task对象),能在适时被系统调用,调度完全由内核决定,所以go的协程调度顺序是未知的。
go语言的sync.WaitGroup控制协程的并发,一般是控制某类协程的并发数,最终决定协程的并发数是P的数量,如果waitGroup的数量超过P是没有作用的(除非都在阻塞,但系统很少这种情况吧?也许?)
go语言任务间通讯使用的是channel,可以减少通讯的性能开销,比如走网络通讯