美文网首页
浅析runtime包中的三个方法Gosched、Goexit、G

浅析runtime包中的三个方法Gosched、Goexit、G

作者: AprilBoy | 来源:发表于2019-08-26 18:00 被阅读0次

    Gosched

    暂停当前goroutine,使其他goroutine先行运算。只是暂停,不是挂起,当时间片轮转到该协程时,Gosched()后面的操作将自动恢复

    未使用Gosched的代码
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        go output("goroutine 2")
        
        output("goroutine 1")
    }
    
    func output(s string){
        for i:=0;i<3;i++{
            fmt.Println(s)
        }
    }
    
    输出
    goroutine 1
    goroutine 1
    goroutine 1
    

    结论:还没等到子协程执行,主协程就已经执行完退出了,子协程将不再执行,所以打印的全部是主协程的数据。当然,实际上这个执行结果也是不确定的,只是大概率出现以上输出,因为主协程和子协程间并没有绝对的顺序关系

    使用Gosched的代码
    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    func main() {
        go output("goroutine 2")
    
        runtime.Gosched()
        output("goroutine 1")
    }
    
    func output(s string){
        for i:=0;i<3;i++{
            fmt.Println(s)
        }
    }
    
    输出
    goroutine 2
    goroutine 2
    goroutine 2
    goroutine 1
    goroutine 1
    goroutine 1
    

    结论:在打印goroutine 1之前,主协程调用了runtime.Gosched()方法,暂停了主协程。子协程获得了调度,从而先行打印了goroutine 2。主协程不是一定要等其他协程执行完才会继续执行,而是一定时间。如果这个时间内其他协程没有执行完,那么主协程将继续执行,例如以下例子

    使用Gosched的代码,并故意延长子协程的执行时间,看主协程是否一直等待
    package main
    
    import (
        "fmt"
        "runtime"
        "time"
    )
    
    func main() {
        go func() {
            time.Sleep(5000)
            output("goroutine 2")
        }()
    
        runtime.Gosched()
        output("goroutine 1")
    }
    
    func output(s string) {
        for i := 0; i < 3; i++ {
            fmt.Println(s)
        }
    }
    
    输出
    goroutine 1
    goroutine 1
    goroutine 1
    

    Goexit

    立即终止当前协程,不会影响其它协程,且终止前会调用此协程声明的defer方法。由于Goexit不是panic,所以recover捕获的error会为nil

    当main方法所在主协程调用Goexit时,Goexit不会return,所以主协程将继续等待子协程执行,当所有子协程执行完时,程序报错deadlock

    package main
    
    import (
        "fmt"
        "runtime"
        "time"
    )
    
    func main() {
        go func(){
            defer func(){
                fmt.Println("defer func executed!")
                fmt.Println("recovered error == ",recover())
            }()
    
            for i:=0;i<3;i++{
                if i==1{
                    runtime.Goexit()
                }
    
                fmt.Println(i)
            }
        }()
    
        time.Sleep(2*time.Second)
    }
    
    输出
    0
    defer func executed!
    recovered error ==  <nil>
    

    GOMAXPROCS(n int) int

    设置可同时执行的逻辑Cpu数量,默认和硬件的线程数一致而不是核心数,可以通过调用GOMAXPROCS(-1)来获取当前逻辑Cpu数

    最好在main函数之前设置它,GOMAXPROCS同时也是go的环境变量之一

    ​ 当n<1:非法数字,方法不作修改

    ​ 当n==1:单核心,多协程并发执行,并发只是看起来是同时执行的,实际上是同一时刻只有一个协程在跑,只是由于cpu的任务调度算法,让多个协程在效果上同时执行

    ​ 当n>1:多核心,多协程并行执行,并行一定是并发,不同的核心同时地跑不同的协程

    想要了解更多有关并发(Concurrency)和并行(Parallel)的区别,可以看看大神Rob Pike的视频Concurrency Is Not Parallelism,里面有很详细的讲解

    作者:胡金生
    出处:www.aprilboy.com
    版权所有,欢迎保留原文链接进行转载:)

    相关文章

      网友评论

          本文标题:浅析runtime包中的三个方法Gosched、Goexit、G

          本文链接:https://www.haomeiwen.com/subject/unvdectx.html