在go中使用sleep时执行哪个goroutine?

时间:2018-09-27 05:38:20

标签: go channel goroutine

我最近在golang是新来的。使用time.sleep函数时,我对goroutine有疑问。代码在这里。

package main

import (
    "fmt"
    "time"
)

func go1(msg_chan chan string) {
    for {
        msg_chan <- "go1"
    }
}
func go2(msg_chan chan string) {
    for {
        msg_chan <- "go2"
    }
}
func count(msg_chan chan string) {
    for {
        msg := <-msg_chan
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}
func main() {
    var c chan string
    c = make(chan string)
    go go1(c)
    go go2(c)
    go count(c)
    var input string
    fmt.Scanln(&input)
}

输出为

go1
go2
go1
go2
go1
go2
我认为当计数功能执行睡眠功能时,go1和go2将以随机顺序执行。所以输出可能像

go1
go1
go2
go2
go2
go1

当我在计数功能中删除睡眠代码时。如我所料,结果是随机的。 我陷入了这个问题。 谢谢。

1 个答案:

答案 0 :(得分:1)

首先要注意的是,共有三个go例程,并且它们彼此独立。将这两个go例程与count例程结合在一起的唯一东西是两个go例程在其上发送值的通道。

time.Sleep没有使go例程同步。在使用time.Sleep时,您实际上是在让count go例程等待那么长的时间,这让其他go例程在count go例程可用的通道上发送值。能够接收。

要检查它的另一件事是增加CPU的数量,这将为您提供随机结果。

func GOMAXPROCS(n int) int
  

GOMAXPROCS设置可以执行的最大CPU数量   同时并返回上一个设置。如果n <1,则不   更改当前设置。本地逻辑CPU的数量   可以使用NumCPU查询计算机。当   调度程序得到改善。

     

可同时执行goroutine的CPU数量为   由GOMAXPROCS Shell环境变量控制,其默认值   value是可用的CPU内核数。带有程序   因此,并行执行的潜力应通过以下方式实现   多CPU计算机上的默认设置。改变并行数   要使用的CPU,设置环境变量或使用类似名称的CPU   运行时软件包的功能来配置运行时支持以   利用不同数量的线程。将其设置为1消除了   真正并行的可能性,迫使独立的goroutines   轮流执行。

考虑go例程的输出是随机的部分,它始终是随机的。但是通道最有可能在队列中工作,该队列是FIFO(先进先出),因为它取决于要接收的b通道上的可用值。因此,无论要发送的通道上可用的值是多少,都让count例程去等待并打印该值。

以一个示例为例,即使我正在使用时间。睡眠输出是随机的:

package main

import (
    "fmt"
    "time"
)

func go1(msg_chan chan string) {
    for i := 0; i < 10; i++ {
        msg_chan <- fmt.Sprintf("%s%d", "go1:", i)
    }
}
func go2(msg_chan chan string) {
    for i := 0; i < 10; i++ {
        msg_chan <- fmt.Sprintf("%s%d", "go2:", i)
    }
}
func count(msg_chan chan string) {
    for {
        msg := <-msg_chan
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}
func main() {
    var c chan string
    c = make(chan string)
    go go1(c)
    go go2(c)
    go count(c)
    time.Sleep(time.Second * 20)
    fmt.Println("finished")
}

这有时会导致race condition,这就是为什么我们通过通道或wait.groups使用同步的原因。

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func go1(msg_chan chan string) {
    defer wg.Done()
    for {
        msg_chan <- "go1"
    }
}
func go2(msg_chan chan string) {
    defer wg.Done()
    for {
        msg_chan <- "go2"
    }
}
func count(msg_chan chan string) {
    defer wg.Done()
    for {
        msg := <-msg_chan
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}
func main() {
    var c chan string
    c = make(chan string)
    wg.Add(1)
    go go1(c)
    wg.Add(1)
    go go2(c)
    wg.Add(1)
    go count(c)
    wg.Wait()
    fmt.Println("finished")
}

现在进入使用永不结束for循环以在通道上发送值的部分。因此,如果删除time.Sleep,则进程将挂起,因为循环将永远不会停止在通道上发送值。