实际上我正在尝试做这样的事情:
我有一个生产者和一个消费者,消费者每隔几分钟检查一次,并统计频道中的事件。但是,当我尝试在操场上进行测试时,我发现:
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")
}
无论我睡多长时间,第二次/ 10次。第二次* 10, 输出将是:
default
0
default
2
default
4
default
6
default
8
default
10
default
stop
10
over
为什么goroutine只能在通道中放置2个事件? 我想要类似的东西:
default
0
default
stop
10
over
问题是通道大小,我只是从其他代码复制而未检查...
答案 0 :(得分:3)
此功能:
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 您的循环回到顶部,并检查{{1}中是否有项目}。存在,因此您的循环将对其进行计数:现在您又获得了两项。您的循环返回顶部,并检查c1
中是否还有其他项目。匿名发件人仍在喘口气,或者类似的话,并且没有在通道中得到 third 值-因此您的循环会检测到该通道为空,并使用c1
子句,这会中断您的循环。
正在运行default
的goroutine现在打印行main
,检查是否应该停止(否),并暂停一秒钟。在所有这些过程中的某个时间(实际上是在暂停时),匿名发件人有机会运行,将一个项目放到通道中,然后在试图将第二个项目放到通道中时阻塞。这只需要几十或数百纳秒。
default
呼叫和您的匿名发送者一样仍被阻止,但是唤醒将在几分之一秒内发生。完成后,您的主循环返回顶部,以运行内部Sleep
循环,该循环从!bk
中读取项目。您现在的状态与上次相同,因此这次您还将从c1
中读取两项。
在程序的其余部分重复此操作。
不能保证此处的几个步骤会以这种方式发生。考虑到当前的实现方式以及您正在单CPU虚拟机上运行所有这些功能的事实,它们只是实际上以 的方式运行。如果这两种情况中的任何一种发生了更改(例如,如果您在多CPU系统上运行或实现被修改),您的程序的行为都可能会更改。
1 实际上,第一次运行时,主例程正在运行,匿名发送者尚未启动,因此计数为零。主goroutine在其c1
调用中被阻止,该调用使匿名发件人可以在单个CPU上运行,从而使您可以通过主例程进行 second 行程。
2 或者,在这种情况下,只要主goroutine有机会再次运行它,就可以。具体取决于游乐场虚拟机的单CPU方面。
答案 1 :(得分:1)
[D] oes在golang中睡眠会阻止其他goroutine吗?
否。
答案 2 :(得分:0)
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")
因为该频道比“选择默认频道”慢。