我最近在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
当我在计数功能中删除睡眠代码时。如我所料,结果是随机的。 我陷入了这个问题。 谢谢。
答案 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
,则进程将挂起,因为循环将永远不会停止在通道上发送值。