# Golang 标准库丨runtime

Package runtime contains operations that interact with Go's runtime system, such as functions to control goroutines. It also includes the low-level type information used by the reflect package; see reflect's documentation for the programmable interface to the run-time type system.

runtime 包包含与 Go 运行时系统交互的操作,比如控制 Goroutine 的函数。它还包括 reflect 包使用的底层类型信息。有关运行时类型系统的可编程接口,请参见 reflect 的文档。

尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括:

  • 内存分配
  • 垃圾回收
  • 栈处理
  • goroutine
  • channel
  • 切片(slice)
  • map
  • 反射(reflection)
  • ...

# 一、常用函数

runtime 调度器是个非常有用的东西,关于 runtime 包几个方法:

  • NumCPU:返回当前系统的 CPU 核数量。

  • GOMAXPROCS:设置最大的可同时使用的 CPU 核数。

    通过 runtime.GOMAXPROCS 函数,应用程序可以在运行期间设置运行时系统中得 P 最大数量。

    但这会引起 Stop the World。所以,应在应用程序最早的调用。并且最好是在运行 Go 程序之前设置好操作程序的环境变量 GOMAXPROCS,而不是在程序中调用 runtime.GOMAXPROCS 函数。

    无论我们传递给函数的整数值是什么值,运行时系统的 P 最大值总会在 1~256 之间。

Go1.8 后,默认让程序运行在多个核上,可以不用设置了。 Go1.8 前,还是要设置一下,可以更高效的利用 CPU。

  • Gosched:让当前线程让出 CPU 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行。

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

  • Goexit:退出当前 goroutine(但是defer语句会照常执行)。

  • NumGoroutine:返回正在执行和排队的任务总数。

    runtime.NumGoroutine 函数在被调用后,会返回系统中的处于特定状态的 Goroutine 的数量。这里的特指是指Grunnable\Gruning\Gsyscall\Gwaition。处于这些状态的 Groutine 即被看做是活跃的或者说正在被调度。

    注意:垃圾回收中所在 Goroutine 的状态也处于这个范围内的话,也会被纳入该计数器。

  • GC:会让运行时系统进行一次强制性的垃圾收集。

    1. 强制的垃圾回收:不管怎样,都要进行的垃圾回收。
    2. 非强制的垃圾回收:只会在一定条件下进行的垃圾回收(即运行时,系统自上次垃圾回收之后新申请的堆内存的单元(也成为单元增量)达到指定的数值)。
  • GOROOT :获取 $GOROOT 目录

  • GOOS : 查看目标操作系统。

    很多时候,我们会根据平台的不同实现不同的操作,就可以用 GOOS 了。

  • Version:获取运行时 Go 版本

# 二、示例代码

  1. 获取 GOROOT 和 OS:

    	fmt.Println(runtime.GOOS)
    	fmt.Println(runtime.GOROOT())
    
  2. 获取 CPU 数量,设置 CPU 数量:

    fmt.Println(runtime.NumCPU())
    runtime.GOMAXPROCS(runtime.NumCPU())
    
  3. 让出 CPU:

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

    输出:

    Goroutine...
    Goroutine...
    Goroutine...
    Goroutine...
    Goroutine...
    main....
    main....
    main....
    main....
    
  4. 终止协程:

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

    输出:

    Goroutine...
    Goroutine...
    Goroutine defer...
    main....
    main....
    main....
    main....
    

参考:

  • https://golang.org/pkg/runtime/
  • https://www.qfgolang.com/?special=bingfagoroutinechannel&pid=2077
上次更新: 8/3/2021, 3:51:50 PM