英文:
Closing stop channel does not stop goroutines
问题
介绍:
我刚开始学习Go语言,并且已经学到了关于并发的课程。
我为自己设定了一个小任务,试图实现我所学到的有关关闭goroutine的知识。
问题:
如果我们关闭通道,它的case
语句将始终在select
语句中被选中,这是向所有goroutine广播取消信号的好方法。
下面有两个goroutine和一个永远不会被接收的quit
通道。
代码在Playground上超时。
如果我在goroutine中注释掉default
部分,那么quit
信号就会被接收到。
问题:
我真的不明白为什么会发生这种情况,也不知道如何解决,尽管我正在尝试。
有人可以解释一下问题,并提供一些建议吗?
package main
import (
"fmt"
"sync"
"time"
)
func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := 1
for {
select {
case <-quit:
fmt.Println("[+]Quiting...")
return
default:
fmt.Printf("%v ", i)
i++
}
}
}
func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := -1
for {
select {
case <-quit:
fmt.Println("[-]Quiting...")
return
default:
fmt.Printf("%v ", i)
i--
}
}
}
func main() {
quit := make(chan struct{})
wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
wg.Add(2)
go positive_numbers(quit, &wg)
go negative_numbers(quit, &wg)
go func(quit chan struct{}) {
defer close(quit)
time.Sleep(1 * time.Second)
}(quit)
wg.Wait()
}
英文:
INTRODUCTION:
I am just starting to learn the Go language and have reached the lesson about concurrency.
I have invented a small task for myself to try to implement what I have learned about closing goroutines.
PROBLEM:
If we close channel it's case
will always be selected in the select
statement, which is a great way to broadcast cancelation signal to all goroutines.
Below I have 2 goroutines and a quit
channel that never gets received.
Code timeouts on Playground.
If I comment out default
part in the goroutines then quit
signal gets received.
QUESTION:
I really do not understand why is this happening and how to fix it, although I am trying.<br/>
Can somebody please explain me what is the problem and offer some advice on how to solve it?<br/>
package main
import (
"fmt"
"sync"
"time"
)
func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := 1
for {
select {
case <-quit:
fmt.Println("[+]Quiting...")
return
default:
fmt.Printf("%v ", i)
i++
}
}
}
func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := -1
for {
select {
case <-quit:
fmt.Println("[-]Quiting...")
return
default:
fmt.Printf("%v ", i)
i--
}
}
}
func main() {
quit := make(chan struct{})
wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
wg.Add(2)
go positive_numbers(quit, &wg)
go negative_numbers(quit, &wg)
go func(quit chan struct{}) {
defer close(quit)
time.Sleep(1 * time.Second)
}(quit)
wg.Wait()
}
答案1
得分: 6
这段代码在实际环境中运行良好;只是与 playground 的“虚拟时间”不兼容。因为 positive_numbers
和 negative_numbers
没有阻塞,运行时无法确定在 Sleep
过期之前每个迭代的次数(基本上,打印操作不消耗模拟的墙钟时间,因此它们试图产生无限输出,在不推进墙钟时间的情况下,你会达到 playground 的 CPU 使用限制)。在真实的机器上,你只能在有限的时间内打印有限的输出,所以会得到合理的行为。
在 playground 中,如果在每次打印之后添加类似 time.Sleep(time.Millisecond)
的代码,你就会强制虚拟时钟向前移动,程序也会终止。
英文:
This code works alright in real life; it's just not compatible with the playground's "fake time" because positive_numbers
and negative_numbers
don't block, and the runtime can't decide how many iterations of each get to run before the Sleep
expires (basically, the prints take zero emulated wallclock time, so they try to produce infinite output, and you hit the playground CPU usage limit without ever advancing the wallclock time at all). On real machines, where you can only print finite output in finite time, you get reasonable behavior.
In the playground, if you add something like time.Sleep(time.Millisecond)
after each print, you force the fake clock to move forward, and the program also terminates.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论