停止循环的代码不起作用
Code for stopping a loop doesn't work
我正在尝试在 Go 中实现循环停止。
我现在拥有的代码的灵感来自这里:
但是,我无法让我的代码按预期运行。来自复杂代码库的我的代码的简化版本是这样的:
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
仅在 cnt == 5
时写入 quit
通道,但不会自行终止。而 runLoop
,如果它在 quit
通道上接收,应该只打印 "quit!"( 而不是 ),但不会自行终止。
为什么我没有得到 "quit!" 输出?我能得到 quit
频道吗?
这个需要如何正确实现
当您尝试在 quit
通道上发送时,quitroutine
会阻塞,直到从中读取数据。
与此同时,runloop
中的主例程正在尝试在 send
频道上发送下一个号码。这也会阻塞,因为从它读取的例程当前被阻塞试图在 quit
通道上发送。
两个例程都被阻塞了,这是一个死锁,所以程序崩溃了。
这可以通过将一个或两个通道发送到 select
或使一个或两个通道缓冲(即使缓冲长度为 1 就足够)来解决。
正如 Adrian 所说,您的一个 goroutine 正试图在 quit
上发送,而另一个正试图在 send
上发送。回答您的问题:
当 cnt == 5
时,quitroutine
开始尝试在 quit
上发送。因为 quit <- struct{}{}
不是 select
的情况,goroutine 将阻塞直到另一个尝试从 quit
读取。另一个 goroutine 同样被困在尝试做 send <- cnt
(当 cnt = 6
时)。
你永远不会得到 "quit!" 输出,因为那个 goroutine 被卡住了试图做 send <-cnt
.
我看到的最简单的解决方案是调整 runLoop()
以便 send <- cnt
成为 select
.
[= 的情况50=]
我会把 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
这似乎主要是您所追求的。
我还想指出 quitroutine()
中的 select
块是不必要的,因为它只有一种情况。清理它可能会更清楚地表明 goroutine 在尝试发送时卡住了,并且永远不会从 send
通道获取输入。
我正在尝试在 Go 中实现循环停止。
我现在拥有的代码的灵感来自这里:
但是,我无法让我的代码按预期运行。来自复杂代码库的我的代码的简化版本是这样的:
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
仅在cnt == 5
时写入quit
通道,但不会自行终止。而runLoop
,如果它在quit
通道上接收,应该只打印 "quit!"( 而不是 ),但不会自行终止。为什么我没有得到 "quit!" 输出?我能得到
quit
频道吗?这个需要如何正确实现
当您尝试在 quit
通道上发送时,quitroutine
会阻塞,直到从中读取数据。
与此同时,runloop
中的主例程正在尝试在 send
频道上发送下一个号码。这也会阻塞,因为从它读取的例程当前被阻塞试图在 quit
通道上发送。
两个例程都被阻塞了,这是一个死锁,所以程序崩溃了。
这可以通过将一个或两个通道发送到 select
或使一个或两个通道缓冲(即使缓冲长度为 1 就足够)来解决。
正如 Adrian 所说,您的一个 goroutine 正试图在 quit
上发送,而另一个正试图在 send
上发送。回答您的问题:
当
cnt == 5
时,quitroutine
开始尝试在quit
上发送。因为quit <- struct{}{}
不是select
的情况,goroutine 将阻塞直到另一个尝试从quit
读取。另一个 goroutine 同样被困在尝试做send <- cnt
(当cnt = 6
时)。你永远不会得到 "quit!" 输出,因为那个 goroutine 被卡住了试图做
send <-cnt
.我看到的最简单的解决方案是调整
[= 的情况50=]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
这似乎主要是您所追求的。
我还想指出 quitroutine()
中的 select
块是不必要的,因为它只有一种情况。清理它可能会更清楚地表明 goroutine 在尝试发送时卡住了,并且永远不会从 send
通道获取输入。