Go语言基础之runtime包

文章引用自

Golang中runtime的使用

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

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

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

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

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

  • NumGoroutine:返回真该执行和排队的任务总数

  • GOOS:目标操作系统

  • GOROOT:返回本机的GO路径

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
 
import "fmt"
import "runtime"
 
func main() {
    fmt.Println("cpus:", runtime.NumCPU())  // 返回当前系统的CPU核数量
    fmt.Println("goroot:", runtime.GOROOT()) //
    fmt.Println("NumGoroutine:", runtime.NumGoroutine()) // 返回真该执行和排队的任务总数
    fmt.Println("archive:", runtime.GOOS) // 目标操作系统
}
 
运行结果
cpus: 12
goroot: /usr/local/go
NumGoroutine: 1
archive: darwin

  

GOMAXPROCS

  // 修改最大可同时使用CPU核数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
import "runtime"
import "time"
 
func init(){
    runtime.GOMAXPROCS(4) // 修改最大可同时使用CPU核数
}
 
func main(){
    t := time.Now().Nanosecond()
    for i:=0;i<100000;i++{
        fmt.Println(i*i*i*i)
    }
    t2 := time.Now().Nanosecond()
    fmt.Println(t2-t)
}

  

Gosched

  // 让当前线程让出cpu以让其他线程运行,它不会挂起当前线程,因此当前线程未来会继续执行

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
}

  

在windows系统上,结果为:

1
2
3
4
a: 0
a: 1
a: 2
a: 3

  

切换成多核,每次运行的结果都不一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main
 
import (
    "fmt"
    "runtime"
)
 
func main() {
    runtime.GOMAXPROCS(4)
    exit := make(chan int)
    go func() {
        defer close(exit)
        go func() {
            fmt.Println("b")
        }()
    }()
 
    for i := 0; i < 10; i++ {
        fmt.Println("a:", i)
 
        if i == 4 {
            runtime.Gosched() //切换任务
        }
    }
    <-exit
}

  

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

GOMAXPROCS和sync配合使用

sync.WaitGroup只有三个方法,Add(),Done()和Wait()。其中Done()是Add(-1)的别名。简单来说,使用Add()添加计数,Done()减少一个计数,计数不为0,阻塞Wait()的运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
 
import (
    "runtime"
    "sync"
    "fmt"
)
 
func main(){
    runtime.GOMAXPROCS(2)
    var wg sync.WaitGroup
    wg.Add(2)
    fmt.Printf("Starting go routines")
    go func(){
        defer wg.Done()
        for char := 'a';char<'a'+26;char++{
            fmt.Printf("%c",char)
        }
    }()
    go func() {
        defer wg.Done()
        for number := 1;number<27;number++{
            fmt.Printf("%d",number)
        }
    }()
 
    fmt.Println("\nWaiting to finish")
    wg.Wait()
    fmt.Println("\n terminating program")
}

  

运行结果:

1
2
3
4
Starting go routines
Waiting to finish
1234567891011121314151617181920212223242526abcdefghijklmnopqrstuvwxyz
 terminating program

  

总结:首先是wg.Add(2)计数为2,阻塞wg.Wait()的运行,然后是wg.Done()减少计数到0,放开wg.Wait()的运行。

 

 

runtime.Caller

  Caller 方法反应的是堆栈信息中某堆栈帧所在文件的绝对路径和语句所在文件的行数。而 skip 表示的是从下往上数第几个堆栈帧。如果要打印全部堆栈信息可以直接使用 debug.PrintStack() 来实现。

  

  func Caller(skip int) (pc uintptr, file string, line int, ok bool)

   参数:skip是要提升的堆栈帧数,0-当前函数,1-上一层函数,....

  

  返回值:

        pc是uintptr这个返回的是函数指针

        file是函数所在文件名目录

        line所在行号

        ok 是否可以获取到信息

示例:

我们分别打印skip为0-3的相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
  
import (
    "fmt"
    "runtime"
)
  
func main() {
    for i := 0 ; i< 4; i++ {
        test(i)
    }
}
  
func test(skip int) {
    call(skip)
}
  
func call(skip int) {
    pc,file,line,ok := runtime.Caller(skip)
    pcName := runtime.FuncForPC(pc).Name()  //获取函数名
    fmt.Println(fmt.Sprintf("%v   %s   %d   %t   %s",pc,file,line,ok,pcName))

 

返回

1
2
3
4
17412399   /Users/songzhibin/go/src/Songzhibin/study/3.go   19   true   main.call
17412303   /Users/songzhibin/go/src/Songzhibin/study/3.go   15   true   main.test
17412294   /Users/songzhibin/go/src/Songzhibin/study/3.go   10   true   main.main
16953069   /usr/local/go/src/runtime/proc.go   203   true   runtime.main

  

  

 

 

 

 

 

posted @   Binb  阅读(2129)  评论(0编辑  收藏  举报
编辑推荐:
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
点击右上角即可分享
微信分享提示