我正在尝试在Go中实现一个循环停止。 我现在的代码的灵感来自这里: how to kill goroutine
但是,我无法让我的代码按预期运行。复杂代码库中我的代码的简化版本是这样的:
package main
import (
"fmt"
"time"
)
var quit chan struct{}
var send chan int
func quitroutine() {
for {
select {
case cnt := <-send:
fmt.Println(cnt)
if cnt == 5 {
quit <- struct{}{}
}
}
}
}
func runLoop() {
cnt := 0
for {
select {
case <-quit:
fmt.Println("quit!")
default:
fmt.Println("default")
}
fmt.Println("inloop")
time.Sleep(1 * time.Second)
cnt++
send <- cnt
}
}
func main() {
quit = make(chan struct{})
send = make(chan int)
go quitroutine()
runLoop()
fmt.Println("terminated")
}
此代码崩溃:
default
inloop
5
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.runLoop()
/tmp/t.go:37 +0x1a6
main.main()
/tmp/t.go:45 +0xa4
goroutine 5 [chan send]:
main.quitroutine()
/tmp/t.go:18 +0x10e
created by main.main
/tmp/t.go:44 +0x9f
exit status 2
问题:
cnt
为5后,为什么会崩溃?如果quitroutine
quit
只写cnt == 5
频道,runLoop
,但不会自行终止。 quit
quit
,如果它在Ex : l = [0,1,2,3,4,5]
l = l[0:5]
now l is [0,1,2,3,4]
频道收到,则应该打印&#34;退出!&#34; (它没有),但不会自行终止。
为什么我不能退出&#34;退出!&#34;输出?我甚至可以获得this.refs.dropDown.select(-1)
频道吗?
如何正确实施
答案 0 :(得分:2)
就像阿德里安所说,你的一个goroutine试图发送quit
,而另一个试图发送send
。回答你的问题:
当cnt == 5
quitroutine
开始尝试发送quit
时。由于quit <- struct{}{}
不是select
,因此goroutine会阻止,直到另一个尝试从quit
读取。另一个goroutine同样试图做send <- cnt
(cnt = 6
时)。
你永远不会退出&#34;退出!&#34;输出,因为goroutine试图做send <-cnt
。
我看到的最简单的解决方案是调整runLoop()
,以便send <- cnt
中的select
成为一个案例。
我将runLoop()
更改为如下所示:
func runLoop() {
cnt := 0
for {
select {
case <-quit:
fmt.Println("quit!")
case send <- cnt: // moved stuff here
fmt.Println("inloop")
time.Sleep(1 * time.Second)
cnt++
default:
fmt.Println("default")
}
// stuff used to be here
}
}
这给了我输出(直到我杀了程序):
default
inloop
0
inloop
1
inloop
2
inloop
3
inloop
4
inloop
5
quit!
default
inloop
6
inloop
7
这似乎主要是你所追求的。
我还要注意select
中的quitroutine()
块是不必要的,因为它只有一个案例。清理它可能会使goroutine在尝试发送时卡住更加清晰,并且从不接受来自send
频道的输入。
答案 1 :(得分:1)
当您尝试发送quit
频道时,quitroutine
会阻止,直到某些内容为止。
与此同时,runloop
中的主例程正在尝试在send
频道上发送下一个号码。这也会阻止,因为从中读取的例程当前被阻止尝试发送quit
频道。
两个例程都被阻止,这是一个死锁,所以程序崩溃了。
这可以通过将一个或两个通道发送到select
中,或者将一个或两个通道缓冲(即使缓冲区长度为1就足够了)来解决。