[英]Why does the goroutine not block for time.Sleep(duration)
[英]does Sleep in golang block other goroutine?
其实我正在尝试做这样的事情:
我有一个生产者和一个消费者,消费者每隔几分钟检查一次并计算频道中的事件。 但是当我尝试在 go 操场上测试它时,我发现:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c1 <- "result 1"
}
quit <- 1
}()
count := 0
for stop := false; !stop; {
for bk := false; !bk; {
select {
case _, ok := <-c1:
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
select {
case <-quit:
fmt.Println("stop")
stop = true
default:
}
fmt.Println(count)
time.Sleep(time.Second / 10)
}
fmt.Println("over")
}
无论我睡多久,从 time.Second/10 到 time.Second*10,output 将是:
default
0
default
2
default
4
default
6
default
8
default
10
default
stop
10
over
为什么 goroutine 只能在通道中放置 2 个事件? 我想要类似的东西:
default
0
default
stop
10
over
问题是频道大小,我只是从其他代码中复制而不检查......
这个 function:
go func() {
for i := 0; i < 10; i++ {
c1 <- "result 1"
}
quit <- 1
}()
总是——嗯,总是在它可以运行的时候——试图将一个字符串放入通道c1
(直到它已经放入十个,无论如何)。
您制作了频道c1
:
c1 := make(chan string, 1)
所以它有空间容纳一个待处理的项目。 因此,如果通道为空,则循环放入一个项目,然后尝试放入第二个项目。 如果此时通道已满——不能保证它是满的,但暂时假设它是满的——这个 goroutine 现在暂停,等待有人将前一个项目从通道中拉出。
同时,在加或减几纳秒的时间——或者可能在其他 goroutine 块1之前或之后——你正在运行另一段代码。 (不能保证确实如此,但事实就是如此。)
for bk := false; !bk; {
select {
case _, ok := <-c1:
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
此代码检查通道中是否有任何内容。 因为匿名发件人将一件物品放入其中,所以频道中确实有一些东西。 此代码删除一个项目,在通道中创建空间。 这会导致匿名发件人中的阻止发送现在运行一步。 不能保证它会这样做,但事实上,它确实会这样做 - 所以现在频道中有另一个项目。
但是,在运行了这一步之后,匿名发件人现在会暂停几纳秒。 2您的循环返回顶部并检查c1
中是否有项目。 有,所以你的循环接受它并计算它:你现在已经接受了另外两个项目。 您的循环返回顶部并检查c1
中是否还有其他项目。 匿名发件人仍在喘气,或者类似的事情,并且没有在通道中获得第三个值 - 因此您的循环检测到通道是空的并采用default
子句,这会在此处中断您的循环。
运行main
的 goroutine 现在打印行default
,检查它是否应该停止(否),并暂停几分之一秒。 在所有这些过程中的某个时间——实际上,在暂停时——匿名发送者有机会运行,将一个项目放入通道,并在它试图将第二个项目放入通道的点处阻塞。 这只需要几十或几百纳秒。
Sleep
呼叫仍被阻止,您的匿名发件人也是如此,但唤醒将在几分之一秒内发生。 当它这样做时,您的主循环将返回顶部,以运行从c1
中读取项目的内部!bk
循环。 您现在位于与上次相同的 state 中,因此您这次也将从c1
中读取两个项目。
这对程序运行的 rest 重复。
这里的几个步骤不能保证以这种方式发生。 考虑到当前的实现以及您在单 CPU 虚拟机上运行所有这些的事实,它们只是碰巧实际上是这样运行的。 如果这两种情况中的任何一种发生变化——例如,如果你在多 CPU 系统上运行,或者实现被修改——你的程序的行为可能会改变。
1事实上,第一次通过时,主程序正在运行,匿名发件人尚未启动,因此计数为零。 主 goroutine 在其Sleep
调用中阻塞,这允许匿名发送者在单个 CPU 上运行,为您第二次通过主例程做好准备。
2或者,在这种情况下,只要主 goroutine 有机会再次运行。 这具体取决于 Playground VM 的单 CPU 方面。
[D] oes 在 golang 中休眠会阻塞其他 goroutine?
不。
for stop := false; !stop; {
for bk := false; !bk; {
select {
case _, ok := <-c1:
// --------- add this ---------
time.Sleep(time.Second / 100)
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
select {
case <-quit:
fmt.Println("stop")
stop = true
default:
}
fmt.Println(count)
time.Sleep(time.Second / 10)
}
fmt.Println("over")
因为通道比“选择默认”慢。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.