尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收(第 10.8 节)、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。

系统信息

  1. runtime.GOARCH // CPU架构
  2. runtime.NumCPU() // CPU数量
  3. runtime.GOOS // 操作系统名称
  4. runtime.GOROOT() // GOROOT
  5. runtime.Version() // go 版本
  6. runtime.NumGoroutine() // 启动的goroutine数

GOMAXPROCS

Golang 默认所有任务都运行在一个 cpu 核里,如果要在 goroutine 中使用多核,可以使用 runtime.GOMAXPROCS 函数修改,当参数小于 1 时使用默认值。

Gosched

这个函数的作用是让当前 goroutine 让出 CPU,当一个 goroutine 发生阻塞,Go 会自动地把与该 goroutine 处于同一系统线程的其他 goroutine 转移到另一个系统线程上去,以使这些 goroutine 不阻塞。

  1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. )
  6. func init() {
  7. runtime.GOMAXPROCS(1) //使用单核
  8. }
  9. func 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. }

output

  1. go run main.go
  2. a: 0
  3. a: 1
  4. b
  5. a: 2
  6. a: 3

多核比较适合那种 CPU 密集型程序,如果是 IO 密集型使用多核会增加 CPU 切换的成本。

Gosched用来让出CPU的时间片。就像是接力赛跑,A跑了一会碰到runtime.Gosched(),就会把时间片给B,让B接着跑。

  1. package main
  2. import (
  3. "runtime"
  4. "fmt"
  5. )
  6. func init() {
  7. runtime.GOMAXPROCS(1)
  8. }
  9. func say(s string){
  10. for i := 0; i < 2; i++ {
  11. runtime.Gosched()
  12. fmt.Println(s)
  13. }
  14. }
  15. func main() {
  16. go say("world")
  17. say("hello")
  18. }

注意结果:

1、先输出了hello,后输出了world.
2、hello输出了2个,world输出了1个(因为第2个hello输出完,主线程就退出了,第2个world没机会了)
把代码中的runtime.Gosched()注释掉,执行结果是:

  1. hello
  2. hello

因为say(“hello”)这句占用了时间,等它执行完,线程也结束了,say(“world”)就没有机会了。
这里同时可以看出,go中的goroutins并不是同时在运行。事实上,如果没有在代码中通过