• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    解决Golang中goroutine执行速度的问题

    突然想到了之前一直没留意的for循环中开goroutine的执行顺序问题,就找了段代码试了试,试了几次后发现几个有意思的地方,我暂时没有精力往更深处挖掘,希望有golang大神能简单说一说这几个地方是怎么回事。

    代码:

    package main  
    import "fmt" 
    func Count(ch chan int) {
    	fmt.Println("Count doing")
    	ch - 1
    	fmt.Println("Counting")
    }
     
    func main() {
        chs := make([]chan int, 100)
    	for i := 0; i  100; i++ {
    		chs[i] = make(chan int)
    		go Count(chs[i])
    		fmt.Println("Count",i)
    	}
    	for i, ch := range chs {
    		-ch
    		fmt.Println("Counting ", i)
    	}
    } 

    试了几次之后,反复的想goroutine执行的问题。

    根据下面的输出,我能看到的是:

    1. for循环的速度 比 for中开出goroutine并执行的速度 执行的快

    2. 但是 开goroutine和执行第一个fmt的速度可能赶上 for循环的速度 比如前12个count和count doing

    3. 关键问题,第二个for循环执行的fmt竟然要比goroutine中的第二个fmt快??(放入channel很耗时?)

    4. main结束时,也就是第二个for循环结束时, 还有goroutine中的第二个fmt没执行

    输出:

    Count 0
    Count 1
    Count 2
    Count 3
    Count 4
    Count 5
    Count 6
    Count 7
    Count 8
    Count 9
    Count 10
    Count 11
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count 12
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count 13
    Count 14
    Count 15
    Count 16
    Count 17
    Count 18
    Count 19
    Count 20
    Count 21
    Count doing
    Count doing
    Count doing
    Count 22
    Count doing
    Count doing
    Count doing
    Count 23
    Count 24
    Count 25
    Count 26
    Count 27
    Count 28
    Count 29
    Count 30
    Count doing
    Count 31
    Count doing
    Count doing
    Count 32
    Count 33
    Count 34
    Count 35
    Count doing
    Count 36
    Count doing
    Count doing
    Count 37
    Count 38
    Count doing
    Count doing
    Count doing
    Count doing
    Count 39
    Count 40
    Count 41
    Count 42
    Count 43
    Count doing
    Count doing
    Count 44
    Count 45
    Count 46
    Count 47
    Count doing
    Count 48
    Count 49
    Count doing
    Count doing
    Count 50
    Count 51
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count 52
    Count 53
    Count doing
    Count doing
    Count doing
    Count doing
    Count 54
    Count doing
    Count 55
    Count 56
    Count 57
    Count 58
    Count 59
    Count 60
    Count 61
    Count 62
    Count 63
    Count 64
    Count 65
    Count doing
    Count doing
    Count doing
    Count 66
    Count 67
    Count 68
    Count 69
    Count doing
    Count 70
    Count doing
    Count 71
    Count 72
    Count doing
    Count 73
    Count doing
    Count doing
    Count 74
    Count doing
    Count 75
    Count 76
    Count 77
    Count doing
    Count doing
    Count doing
    Count doing
    Count 78
    Count 79
    Count 80
    Count 81
    Count 82
    Count 83
    Count 84
    Count 85
    Count 86
    Count 87
    Count 88
    Count 89
    Count 90
    Count 91
    Count 92
    Count 93
    Count 94
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count 95
    Count doing
    Count 96
    Count doing
    Count 97
    Count 98
    Count doing
    Count 99
    Count doing
    Count doing
    Counting  0
    Counting  1
    Counting  2
    Counting  3
    Counting  4
    Counting  5
    Counting  6
    Count doing
    Count doing
    Counting  7
    Counting  8
    Count doing
    Counting
    Count doing
    Counting  9
    Counting
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Counting
    Count doing
    Count doing
    Count doing
    Counting
    Count doing
    Counting
    Count doing
    Counting  10
    Counting  11
    Counting
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Count doing
    Counting
    Count doing
    Count doing
    Counting
    Counting
    Count doing
    Count doing
    Count doing
    Count doing
    Counting
    Count doing
    Counting
    Count doing
    Count doing
    Counting  12
    Counting  13
    Counting  14
    Counting  15
    Counting  16
    Counting  17
    Counting  18
    Counting  19
    Counting  20
    Counting  21
    Counting  22
    Counting  23
    Counting  24
    Counting  25
    Counting  26
    Counting  27
    Counting  28
    Counting  29
    Counting  30
    Counting  31
    Counting  32
    Counting  33
    Counting  34
    Counting  35
    Counting  36
    Counting  37
    Counting  38
    Counting  39
    Counting  40
    Counting  41
    Counting  42
    Counting  43
    Counting  44
    Counting  45
    Counting  46
    Counting  47
    Counting  48
    Counting  49
    Counting  50
    Counting  51
    Counting  52
    Counting  53
    Counting  54
    Counting  55
    Counting  56
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Count doing
    Counting
    Count doing
    Counting
    Counting
    Counting  57
    Counting  58
    Counting  59
    Counting  60
    Counting  61
    Counting  62
    Counting  63
    Counting  64
    Counting  65
    Counting  66
    Counting  67
    Counting  68
    Counting  69
    Counting  70
    Counting  71
    Counting  72
    Counting  73
    Counting  74
    Counting  75
    Counting  76
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting
    Counting  77
    Counting  78
    Counting  79
    Counting  80
    Counting  81
    Counting  82
    Counting  83
    Counting  84
    Counting  85
    Counting  86
    Counting  87
    Counting  88
    Counting  89
    Counting  90
    Counting  91
    Counting  92
    Counting  93
    Counting  94
    Counting  95
    Counting  96
    Counting  97
    Counting  98
    Counting  99

    补充:【golang】goroutine调度的坑

    今天说说我遇到的一个小坑, 关于goroutine 调度的问题。

    关于goroutine的调度,网上资料已经一大堆了,这里就不再赘述了。

    还是简单的说一下我理解的goroutine的调度。goroutine是语言层面的,它和内核线程是M:N的关系,并且用了分段栈,是相当轻量了。

    如果设置runtime.GOMAXPROCS为1,那么会有一个上下文G,在G上会有一个对应的内核线程M,内核线程M上可以对应很多个goroutine记作G,每个上下文都会有一个队列称作runqueue,在用go关键字开启一个goroutine的时候,该goroutine就会被装入runqueue中,然后被M用来执行,如果刚好有两个goroutine在队列里,先执行的goroutine因为执行一些耗时操作(系统调用,读写 channel,gosched 主动放弃,网络IO)会被挂起(扔到全局runqueue),然后调度后面的goroutine。

    好,重点在这里,看一下下面的一段代码

    func main(){
        runtime.GOMAXPROCS(1)
        waitGroup.Add(1)
        go func(){
            defer waitGroup.Done()
            for i := 0;i  20;i++ {
                fmt.Println("hello")
                f, _ := os.Open("./data")
                f.Write([]byte("hello"))
            }
        }()
        waitGroup.Add(1)
        go func(){
            defer waitGroup.Done()
            for {
            }
        }()
        waitGroup.Wait()
    }
    

    这段代码你运行,你会发现,永远都会被阻塞住,hello永远都打印不出来

    好,这里出现了两个问题

    1.为什么死循环的goroutine总是先运行?按理说不应该是随机的吗?

    2.为什么死循环的goroutine会阻塞而没有被挂起?

    先看第二个问题。这里的话,我当时也很苦恼,于是在网上发了问题,得到的回复是,死循环不属于上述任何一种需要被挂起的状态,于是死循环的goroutine会一直运行,想象一个高并发的场景,如果其中一个goroutine因为某种原因陷入死循环了,当前执行这个goroutine的OS thread基本就会被一直执行这个goroutine,直到程序结束,这简直是一场灾难。但是,1.12 会修正这个小问题。我们还是默默的等待新版本发布吧。

    再看第一个问题。为什么死循环的goroutine总是先运行?按理说不应该是随机的吗?测试过很多次,都是第二个goroutine先运行。嗯,其实就算是第二个goroutine先运行也是具有随机性的,这关于golang的编译器如何去实现随机。看一下大佬的回答 :

    不是说测试很多遍它就会一直这样,语言规范没有说必须是这个顺序,那编译器怎么实现都可以,因为都不违反规范。

    所以你要把它看作是随机的,不能依赖这种未确定的行为,不然很可能新版的编译器就会破坏你依赖的事实。有些项目不敢升级编译器版本,就是因为依赖了特定版本的编译器的行为,一升级就坏了。

    不是你自己测试很多遍你就能依赖它,编译器、操作系统、硬件等等不同,都有可能出现不同的结果。可以依赖的只有语言规范( https://golang.org/ref/spec ),编译器实现者是一定会遵守的。

    到这里也算是解决了上述的两个问题了。

    来看一下另外一个版本

    func main(){
        runtime.GOMAXPROCS(1)
        waitGroup.Add(1)
        go func(){
            defer waitGroup.Done()
            for {
            }
        }()
        waitGroup.Add(1)
        go func(){
            defer waitGroup.Done()
            for i := 0;i  20;i++ {
                fmt.Println("hello")
                f, _ := os.Open("./data")
                f.Write([]byte("hello"))
                http.Get("http://www.baidu.com")
                fmt.Println("request successful")
            }
        }()
        waitGroup.Wait()
    }
    

    执行结果是,会先打印一个hello,然后陷入死循环,这也是说明了goroutine在遇到耗时操作或者系统调用的时候,后面的代码都不会执行了(request successful 没有被打印),会被抛到全局runqueue里去,然后执行runqueue中等待的goroutine

    希望能够帮助和我一样正在学习golang的友军们更好的理解goroutine的调度问题

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • golang中for循环遍历channel时需要注意的问题详解
    • golang实现基于channel的通用连接池详解
    • Golang优雅关闭channel的方法示例
    • golang中单向channel的语法介绍
    • golang gin 框架 异步同步 goroutine 并发操作
    • GOLANG使用Context管理关联goroutine的方法
    • 关于golang利用channel和goroutine完成统计素数的思路
    上一篇:解决golang结构体tag编译错误的问题
    下一篇:golang 比较浮点数的大小方式
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    解决Golang中goroutine执行速度的问题 解决,Golang,中,goroutine,执行,