Go 协程机制
Thread vs. Groutine
协程是一个更轻量级的线程
1.创建时默认的stack的大小
JDK5 以后JAVA Thread stack 默认为1M
Groutine的Stack初始化大小为2K 创建起来会更快
2.和KSE(Kernel Space Entity)的对应关系
JAVA Thread是1:1
Groutine是M:N
Kernel Space Entity 系统线程由CPU直接调度,调度效率高
线程切换的时候,会引起内核线程context切换的行为,大消耗,协程多对一,所以切换的消耗更小
Go基本调度机制
M 系统线程 kernel entity
P Processsor Go语言实现的协程处理器
G Goroutine 协程
每一个Go Processor都挂在系统线程上,并且有一个协程执行队列,依次运行协程,一个协程运行的事件特别长,Go会有 守护进程计数,记录每个Processor完成的协程的数量 ,完成的协程数量在一段时间内没有明显的变化,就会在协程任务栈上插入一个明显的标记,当协程运行的时候遇到非内联函数,就会读到这个标记,就会中断自己,把自己插入到等待协程的任务队列中,切换成别的协程。
另一个提升并发数量的机制设计的是:系统中断了I/O等,需要等待的时候,为了提高整体并发,Processor会把自己移动到另一个可以使用的System Thread当中,继续执行他所挂载的其他队列中的协程,当被中断的协程被唤醒,会把自己加入到某个Processor的等待队列中,或者加入到全局等待队列中,当一个协程被中断的时候,它的寄存器状态也会保存到这个协程对象里,当协程再次获得运行的机会时,重新写入寄存器,继续运行。
Go的携程机制和系统线程多对多的关系,如何高效的利用系统线程,尽量多的运行并发的协程任务
示例代码
Go 协程 关键字是go
package goroutine_test
import (
"fmt"
"testing"
"time"
)
func TestGroutine(t *testing.T){
for i:=0;i<10;i++{
go func (i int){
fmt.Println(i)
}(i)
/*
如果使用匿名变量 不在()内填写,就会导致出现竞态条件,使用锁机制完成
在Test以及它启动的协程上,被共享了,必须用锁
Go的方法调用的传递都是值传递,传递i的同时复制了i的值,每个携程上拥有的i的地址是不同的
不会出现竞争关系,可以正确执行但是乱序而已
*/
}
time.Sleep(time.Millisecond*20)
//协程的调度顺序和方法的调度顺序不相同
}