堕落不振功业废,勤耕不辍日月新

golang runtime进程调度函数

Python hailen 8℃

尽管 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函数的示例代码,如下:

package main
import (
    "fmt"
)
func main() {
    go func() { //子协程   //没来的及执行主进程结束
        for i := 0; i < 5; i++ {
            fmt.Println("go")
        }
    }()
    for i := 0; i < 2; i++ { //默认先执行主进程主进程执行完毕
        fmt.Println("hello")
    }
}

该代码运行后的结果如下:

[root@361way runtime]# go run 01-sched.go
hello
hello

再看一个使用了Gosched函数的结果:

package main
import (
    "fmt"
    "runtime"
)
func main() {
    go func() {  //让子协程先执行
        for i := 0; i < 5; i++ {
            fmt.Println("go")
        }
    }()
    for i := 0; i < 2; i++ {
        //让出时间片,先让别的协议执行,它执行完,再回来执行此协程
        runtime.Gosched()
        fmt.Println("hello")
    }
}

代码运行结果如下:

[root@361way runtime]# go run 02-sched.go
go
go
go
go
go
hello
hello

三、Goexit()

示例代码如下:

package main
import (
	"fmt"
	"runtime"
)
func test() {
	defer fmt.Println("ccccccccccccc")
	//return //终止此函数
	runtime.Goexit() //终止所在的协程
	fmt.Println("dddddddddddddddddddddd")
}
func main() {
	//创建新建的协程
	go func() {
		fmt.Println("aaaaaaaaaaaaaaaaaa")
		//调用了别的函数
		test()
		fmt.Println("bbbbbbbbbbbbbbbbbbb")
	}() //别忘了()
	//特地写一个死循环,目的不让主协程结束
	for {
	}
}

调用结果如下:

[root@361way runtime]# go run 03-goexit.go
aaaaaaaaaaaaaaaaaa
ccccccccccccc
^Csignal: interrup

可以看出只调用了aaa和ccc相关的行。后面的ddd和bbb相关的行由于所在的协程被终止了,所以后面的结果无法执行。

四、GOMAXPROCS()

package main
import (
    "fmt"
    "runtime"
)
func init() {
    runtime.GOMAXPROCS(1)  //使用单核
}
func main() {
    exit := make(chan int)
    go func() {
        defer close(exit)
        go func() {
            fmt.Println("b")
        }()
    }()
    for i := 0; i < 4; i++ {
        fmt.Println("a:", i)
        if i == 1 {
            runtime.Gosched()  //切换任务
        }
    }
    <-exit
}

上面的代码测试结果如下:

[root@361way runtime]# go run maxprocs.go
a: 0
a: 1
b
a: 2
a: 3

将runtime.GOMAXPROCS(1) 修改为runtime.GOMAXPROCS(4)再测试时,结果如下:

[root@361way runtime]# go run maxprocs.go
a: 0
a: 1
a: 2
a: 3

注意:上面的CPU核数修改时,要保证测试主机自身的核数多于4,不然还是会输出上面带b的结果。

golang runtime进程调度函数,首发于运维之路

转载请注明:我是IT » golang runtime进程调度函数

喜欢 (0)or分享 (0)