首页 > 解决方案 > 在 go 中使用 sleep 时执行哪个 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

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

标签: gochannelgoroutine

解决方案


首先要注意的是,共有三个 goroutine,它们都是相互独立的。唯一将这两个 go 例程与 count 例程结合在一起的是两个 go 例程都在其上发送值的通道。

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

您可以做的另一件事是增加 CPU 的数量,这将为您提供随机结果。

func GOMAXPROCS(n int) int

GOMAXPROCS 设置可以同时执行的最大 CPU 数量并返回之前的设置。如果 n < 1,则不会更改当前设置。可以使用 NumCPU 查询本地机器上逻辑 CPU 的数量。当调度程序改进时,此调用将消失。

可同时执行 goroutine 的 CPU 数量由 GOMAXPROCS shell 环境变量控制,其默认值是可用的 CPU 内核数。因此,具有并行执行潜力的程序应该默认在多 CPU 机器上实现它。要更改要使用的并行 CPU 数量,请设置环境变量或使用运行时包的类似名称函数来配置运行时支持以利用不同数量的线程。将其设置为 1 消除了真正并行的可能性,迫使独立的 goroutine 轮流执行。

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

举个例子,即使我使用 time.Sleep 输出是随机的:

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

这有时会导致竞争条件,这就是我们使用通道或 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您的进程将挂起,因为循环将永远不会停止在通道上发送值。


推荐阅读