英文:
Passing any value over cancel channel causes program to hang
问题
我正在尝试使用工作协程来检查到一定距离的直径,在一次只处理一个瓦片时效果很好,但是当处理多个瓦片时,速度会显著下降。我猜想这是因为即使不再需要,很多协程仍在运行。我添加了一个失败通道来告诉所有正在运行的协程关闭,但这样做会导致应用程序挂起。实际上,即使我不消耗通道的值,将任何值传递给通道也会导致应用程序挂起。
下面是使用通道的例程:
caught := 0
loop:
for angle := float64(0); angle < 360; angle++ {
select {
case <-failChannel:
break loop
default:
log.Print(angle)
}
}
channelOut <- []int{radius, caught}
这是使用通道的循环:
for {
select {
case circle := <-channelOut:
if circle[1] == 0 {
radiusMap[circle[0]] = 0
if _, radius := testLine(radiusMap); radius <= circle[0] {
failChannel <- 0
}
} else {
radiusMap[circle[0]] = 1
}
default:
}
}
这个循环将整数传递给failChannel。我使用整数通道是因为我想看看较小的半径是否失败,如果失败则停止。目前我只是将其设置为任意整数进行测试。
有人知道为什么会出现挂起的情况吗?对我来说这似乎没有道理。
英文:
I am trying to use worker goroutines to check diameters out to a certain distance, it works great when I do one tile at a time, but when I do a bunch, there is a massive slow down. I figure this is because a lot of goroutines are still running even though they are no longer needed. I added a fail channel to tell all running goroutines to close up shop, but doing this causes the app to hang. Actually, passing any value to the channel causes the app to hang, even if I don't consume it.
caught := 0
loop:
for angle := float64(0); angle < 360; angle++ {
select {
case <-failChannel:
break loop
default:
log.Print(angle)
}
}
channelOut <- []int{radius, caught}
is the routine that uses the channel
for {
select {
case circle := <-channelOut:
if circle[1] == 0 {
radiusMap[circle[0]] = 0
if _, radius := testLine(radiusMap); radius <= circle[0] {
failChannel <- 0
}
} else {
radiusMap[circle[0]] = 1
}
default:
}
}
Is the loop that will pass the int to the failChannel. I am doing an int channel because I want to see if a lower radius failed, and if so stop. I made it just any int right now as a test.
Does anyone have any clue why this would be hanging? It doesn't seem to make sense to me.
答案1
得分: 2
你可以关闭一个通道来表示完成:
failed := make(chan struct{})
select {
case <-failed:
}
在另一个 goroutine 中:
close(failed)
这将导致 failed
case 发生。这适用于任意数量的 goroutine 监听 failed
。但要小心,只能执行一次关闭操作,因为关闭一个已经关闭的通道会引发 panic。你可以使用以下模式:
// Stopper 通过在调用 Stop 时关闭 C 来表示完成
type Stopper struct {
C chan struct{}
once sync.Once
}
// NewStopper 创建一个新的 Stopper
func NewStopper() *Stopper {
return &Stopper{
C: make(chan struct{}),
}
}
// Stop 关闭 C。可以安全地多次调用
func (s *Stopper) Stop() {
s.once.Do(func() {
close(s.C)
})
}
英文:
You can close a channel to signal completion:
failed := make(chan struct{})
select {
case <-failed:
}
In another goroutine:
close(failed)
Will cause the failed
case to happen. This will work with any number of goroutines listening on failed
. Be careful to only do this once though, because closing an already close channel will panic. You could use this pattern:
// A Stopper signals completion over C by closing it when Stop is called
type Stopper struct {
C chan struct{}
once sync.Once
}
// NewStopper creates a new Stopper
func NewStopper() *Stopper {
return &Stopper{
C: make(chan struct{}),
}
}
// Stop closes C. It is safe to call multiple times
func (s *Stopper) Stop() {
s.once.Do(func() {
close(s.C)
})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论