• Go语言基础之runtime包


    文章引用自

    Golang中runtime的使用

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

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

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

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

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

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

    • GOOS:目标操作系统

    • GOROOT:返回本机的GO路径
    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时使用默认值。

    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不阻塞。

    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系统上,结果为:

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

      

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

    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()的运行。

    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("
    Waiting to finish")
        wg.Wait()
        fmt.Println("
     terminating program")
    }
    

      

    运行结果:

    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的相关信息

    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))
    

    返回

    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
    

      

      

    Songzhibin
  • 相关阅读:
    信息安全学习笔记1
    Linux学习笔记4------磁盘分区(问答形式)
    Linux学习笔记3-------添加磁盘并分区
    用for循环打印三角形
    包机制
    运算符
    变量、常量、作用域
    数据类型转换
    数据的基本类型
    Java运行原理的简单理解
  • 原文地址:https://www.cnblogs.com/binHome/p/11928542.html
Copyright © 2020-2023  润新知