golang runtime进程调度函数
尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。
最近在写一个http server里涉及到for循环时,在查找解决方案时,从网上查看到的结果有提到runtime.Gosched (该问题请查看stackoverflow)。runtime下的函数和方法比较多,而本篇只介绍runtime下的三个函数:Gosched、Goexit、GOMAXPROCS。
一、runtime调度三函数的介绍
runtime.Gosched(),用于让出CPU时间片,让出当前goroutine的执行权限,调度器安排其它等待的任务运行,并在下次某个时候从该位置恢复执行。这就像跑接力赛,A跑了一会碰到代码runtime.Gosched()就把接力棒交给B了,A歇着了,B继续跑。
runtime.Goexit(),调用此函数会立即使当前的goroutine的运行终止(终止协程),而其它的goroutine并不会受此影响。runtime.Goexit在终止当前goroutine前会先执行此goroutine的还未执行的defer语句。请注意千万别在主函数调用runtime.Goexit,因为会引发panic。
runtime.GOMAXPROCS(),用来设置可以并行计算的CPU核数最大值,并返回之前的值。默认此函数的值与 CPU 逻辑个数相同,即有多少个goroutine并发执行,当然可以设置它,它的取值是1~256。最好在主函数在开始前设置它,因为设置它会停止当前程序的运行。
注意:GO默认是使用一个CPU核的,除非设置runtime.GOMAXPROCS 。在多核环境下,什么情况下设置runtime.GOMAXPROCS会比较好的提高速度呢?适合于CPU密集型、并行度比较高的情景。如果是IO密集型,CPU之间的切换也会带来性能的损失。
二、Gosched()
先看下面一个没有使用Gosched函数的示例代码,如下:
1package main
2import (
3 "fmt"
4)
5func main() {
6 go func() { //子协程 //没来的及执行主进程结束
7 for i := 0; i < 5; i++ {
8 fmt.Println("go")
9 }
10 }()
11 for i := 0; i < 2; i++ { //默认先执行主进程主进程执行完毕
12 fmt.Println("hello")
13 }
14}
该代码运行后的结果如下:
1[root@361way runtime]# go run 01-sched.go
2hello
3hello
再看一个使用了Gosched函数的结果:
1package main
2import (
3 "fmt"
4 "runtime"
5)
6func main() {
7 go func() { //让子协程先执行
8 for i := 0; i < 5; i++ {
9 fmt.Println("go")
10 }
11 }()
12 for i := 0; i < 2; i++ {
13 //让出时间片,先让别的协议执行,它执行完,再回来执行此协程
14 runtime.Gosched()
15 fmt.Println("hello")
16 }
17}
代码运行结果如下:
1[root@361way runtime]# go run 02-sched.go
2go
3go
4go
5go
6go
7hello
8hello
三、Goexit()
示例代码如下:
1package main
2import (
3 "fmt"
4 "runtime"
5)
6func test() {
7 defer fmt.Println("ccccccccccccc")
8 //return //终止此函数
9 runtime.Goexit() //终止所在的协程
10 fmt.Println("dddddddddddddddddddddd")
11}
12func main() {
13 //创建新建的协程
14 go func() {
15 fmt.Println("aaaaaaaaaaaaaaaaaa")
16 //调用了别的函数
17 test()
18 fmt.Println("bbbbbbbbbbbbbbbbbbb")
19 }() //别忘了()
20 //特地写一个死循环,目的不让主协程结束
21 for {
22 }
23}
调用结果如下:
1[root@361way runtime]# go run 03-goexit.go
2aaaaaaaaaaaaaaaaaa
3ccccccccccccc
4^Csignal: interrup
可以看出只调用了aaa和ccc相关的行。后面的ddd和bbb相关的行由于所在的协程被终止了,所以后面的结果无法执行。
四、GOMAXPROCS()
1package main
2import (
3 "fmt"
4 "runtime"
5)
6func init() {
7 runtime.GOMAXPROCS(1) //使用单核
8}
9func main() {
10 exit := make(chan int)
11 go func() {
12 defer close(exit)
13 go func() {
14 fmt.Println("b")
15 }()
16 }()
17 for i := 0; i < 4; i++ {
18 fmt.Println("a:", i)
19 if i == 1 {
20 runtime.Gosched() //切换任务
21 }
22 }
23 <-exit
24}
上面的代码测试结果如下:
1[root@361way runtime]# go run maxprocs.go
2a: 0
3a: 1
4b
5a: 2
6a: 3
将runtime.GOMAXPROCS(1) 修改为runtime.GOMAXPROCS(4)再测试时,结果如下:
1[root@361way runtime]# go run maxprocs.go
2a: 0
3a: 1
4a: 2
5a: 3
注意:上面的CPU核数修改时,要保证测试主机自身的核数多于4,不然还是会输出上面带b的结果。
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/go-runtime-process/5946.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.