golang 的协程调度
众所周知,golang 作为协程调度模型,是非抢占式而是自主放弃式的。
我的理解是,当一个协程进行 IO 的阻塞操作时,就会让出 CPU,让调度程序来调度其他协程来进行操作
调度程序并不会因为你的实际调用时间过长就干掉你,如果你觉得自己调用时间太长,可以用 runtime 库的 Gosched () 让出 CPU
但实际的测试(基于 1.7 版本)和之前的理解有差距,测试过程是递进的,可以直接跳过看结论
测试
协程是否独占直到自己运行完毕版本 1
1 | package main |
这段代码执行两个协程,但是用 GOMAXPROCS 限定了只用单核
预期是 test1 运行完输出,然后执行 test2 再运行完输出
但实际结果是
1 | test1 1000000000 1000000000 2017-07-24 09:51:41.577265937 +0800 CST |
然后我就误以为是在轮流执行
协程是否独占到运行完毕版本 2
随后我测试了这一段代码
1 | package main |
输出
1 | test2 1000000000 |
如果按照测试代码 1 的结论,两边轮流执行直到结束,那应该输出的是 test2, 0
然而现在并不是的,test1 执行完了才执行了 test2
于是我只能猜测 fmt.Println 函数会造成协程挂起,隐式的调用了 Gosched ()
这么一想,似乎就能说得通了,重看测试代码 1.1
test1 执行循环结束到 fmt 的时候挂起了,随后 test2 执行循环结束到 fmt 的时候又挂起了
此时 i,j 都是 1000000000,然后依次调用 test1 的 fmt 和 test2 的 fmt,输出相同的时间和 i,j
但是新的问题又来了,时间为什么会相同呢?调用 fmt 会挂起,通过测试代码 1.2 应该能确认了
但是对于函数调用来说,参数会比函数先运行,因此测试代码 1.1 的两个函数的 time.Now () 记录的时间值,不应该是一样的
难道。。time.Now () 函数也会引起挂起??
time.Now () 函数是否会引起挂起
这一次的测试,在 1.2 的函数 test1 循环体中加入 time.Now ()
1 | func test1() { |
运行结果
1 | test2 493747 |
果然切换了,因为这里的 i 是 493747(为什么不是 0?稍后讨论),并且程序执行了很久才退出
这就说明 test1 执行了一半去执行了 test2
这就很奇怪了,如果说 fmt 引起和标准输出的交互,从而导致切换也就算了
time.Now () 凭什么切,难道申请内存就会切换??
申请内存是否会切换
这一次测试,在 1.2 的函数 test1 循环体中加入了申请内存
1 | func test1() { |
输出
1 | test2 957470 |
果然这一次也切换了,验证了申请内存也会导致切换
但是切换的 i 不是 1,也就是说并不是每一次调用都会切换。而是按照时间来的。
单协程运行一段时间后,如果调用申请内存操作,就会被调度程序切换协程
测试调度程序切换时间
1 | var t time.Time |
输出基本是 10ms 以上波动一点点
总结
对一段纯计算的代码,调度程序并不会因为你的实际调用时间过长就干掉你,如果你觉得自己调用时间太长,可以用 runtime 库的 Gosched () 让出 CPU
(基于 1.7 版本) 你在计算的代码中申请了内存,那么如果已经运行了超过 10ms,就可能被调度程序切换走,这也是为了防止饥饿吧
未找到相关的 Issues 进行评论
请联系 @tedcy 初始化创建