go - 在 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
当我删除计数功能中的睡眠代码时。结果如我所料,是随机的。我被困在这个问题上。谢谢。
解决方案
首先要注意的是,共有三个 goroutine,它们都是相互独立的。唯一将这两个 go 例程与 count 例程结合在一起的是两个 go 例程都在其上发送值的通道。
time.Sleep
没有使 go 例程同步。在使用time.Sleep
时,您实际上是让count
go 例程等待那么长时间,这让其他 go 例程在通道上发送值,该值可供count
go 例程能够接收。
您可以做的另一件事是增加 CPU 的数量,这将为您提供随机结果。
func GOMAXPROCS(n int) int
GOMAXPROCS 设置可以同时执行的最大 CPU 数量并返回之前的设置。如果 n < 1,则不会更改当前设置。可以使用 NumCPU 查询本地机器上逻辑 CPU 的数量。当调度程序改进时,此调用将消失。
可同时执行 goroutine 的 CPU 数量由 GOMAXPROCS shell 环境变量控制,其默认值是可用的 CPU 内核数。因此,具有并行执行潜力的程序应该默认在多 CPU 机器上实现它。要更改要使用的并行 CPU 数量,请设置环境变量或使用运行时包的类似名称函数来配置运行时支持以利用不同数量的线程。将其设置为 1 消除了真正并行的可能性,迫使独立的 goroutine 轮流执行。
考虑到 go 例程的输出是随机的部分,它总是随机的。但是通道很可能在 FIFO(先进先出)队列中工作,因为它取决于 b 接收到的通道上可用的值。因此,无论要发送的通道上可用的值是什么,都让count
go 例程等待并打印该值。
举个例子,即使我使用 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
您的进程将挂起,因为循环将永远不会停止在通道上发送值。
推荐阅读
- python - 使用加权类处理 GradientBoostingClassifier 中的不平衡数据?
- python - Python BeautifulSoup 选择属性以开头的所有元素
- node.js - 从 node.js 创建联系人到 mautic uisng Mautic rest api
- javascript - 在 Javascript 中的 obj 数组中进行高效搜索
- javascript - 使用 Sinon,当 http 是属性时,我如何模拟 http.foo.get?
- c - C 中涉及套接字和客户端-服务器通信的程序问题
- javascript - Angular:如何检测图像方向后视或当我从数据库中获取图像时?
- python - 切片列表以获得相同长度的四分之一
- ios - 在 App Store 上提交应用程序徽标的问题
- swift - 如何在表格视图单元格上的集合视图数据源中调用多个 CollectionView