3 回答
TA贡献1898条经验 获得超8个赞
这个功能:
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,这会在此处中断您的循环。
现在运行的 goroutine 会main打印该行default,检查是否应该停止(否),并暂停几分之一秒。在所有这一切过程中的某个时间(实际上是在暂停时),匿名发送者有机会运行,将一个项目放入通道中,并在尝试将第二个项目放入通道中时阻塞。这只需要几十或几百纳秒。
呼叫Sleep仍然被阻止,就像您的匿名发件人一样,但唤醒将在几分之一秒内发生。当它发生时,您的主循环将返回到顶部,以运行!bk从 中读取项目的内部循环c1。您现在处于与上次相同的状态,因此这次您还将阅读两项c1。
程序运行的其余部分会重复此操作。
这里的几个步骤不能保证以这种方式发生。考虑到当前的实现以及您在单 CPU 虚拟机上运行所有这些的事实,它们实际上只是碰巧以这种方式运行。如果这两种情况中的任何一个发生变化(例如,如果您在多 CPU 系统上运行,或者实现被修改),您的程序的行为可能会发生变化。
1事实上,第一次通过时,主例程正在运行,匿名发送者尚未启动,因此计数为零。主 goroutine 会阻塞在其Sleep调用中,这允许匿名发送者在单个 CPU 上运行,为您第二次访问主例程做好准备。
2或者,在本例中,只要主 Goroutine 给它再次运行的机会即可。这具体取决于 Playground VM 的单 CPU 方面。
TA贡献1744条经验 获得超4个赞
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")
因为该通道比“选择默认”慢。
- 3 回答
- 0 关注
- 199 浏览
添加回答
举报