英文:
How can I pause and resume an operation that is running in a goroutine
问题
我有一个简单的Go应用程序,它从标准输入读取并回显文本。
如果输入是start
,我会启动一个goroutine,将一些递增的数字输出到标准输出。
我想要能够停止或暂停这个goroutine的运行。我创建了一个通道来发送信号,但出于某种原因它仍然在运行并且不会退出。
我猜测在select语句中的默认情况下会一直运行而不返回?
我该如何修复这个问题?
package main
import (
"bufio"
"fmt"
"os"
"strings"
"time"
)
func main() {
fmt.Println("starting...")
reader := bufio.NewReader(os.Stdin)
quit := make(chan bool)
for {
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
fmt.Printf("entered: %s\n", text)
switch {
case text == "start":
fmt.Println("starting")
go counter(1, 1, quit)
case text == "pause":
fmt.Println("pausing")
case text == "resume":
fmt.Println("resuming")
case text == "stop":
fmt.Println("stopping")
quit <- true
}
}
}
func counter(startFrom int, multiplyBy int, ch <-chan bool) {
for {
select {
case <-ch:
return
default:
for x := startFrom; x < 100; x++ {
time.Sleep(time.Millisecond * 1000)
result := x * multiplyBy
fmt.Printf("%d", result)
}
}
}
}
英文:
I have a simple go app that reads from standard input and echos the text.
If the input is start
I fire off a goroutine that outputs some incrementing numbers to standard out.
I want to be able to stop, pause this go routine from running. I created a channel that sends the signal but for some reason it keeps running and doesn't quit.
I'm guessing the default case in the select keeps running and doesn't return?
How can I fix this?
package main
import (
"bufio"
"fmt"
"os"
"strings"
"time"
)
func main() {
fmt.Println("starting...")
reader := bufio.NewReader(os.Stdin)
quit := make(chan bool)
for {
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
fmt.Printf("entered: %s\n", text)
switch {
case text == "start":
fmt.Println("starting")
go counter(1, 1, quit)
case text == "pause":
fmt.Println("pausing")
case text == "resume":
fmt.Println("resuming")
case text == "stop":
fmt.Println("stopping")
quit <- true
}
}
}
func counter(startFrom int, multiplyBy int, ch <-chan bool) {
for {
select {
case <-ch:
return
default:
for x := startFrom; x < 100; x++ {
time.Sleep(time.Millisecond * 1000)
result := x * multiplyBy
fmt.Printf("%d", result)
}
}
}
}
答案1
得分: 3
你猜对了。一旦你的程序进入default
分支,它就不会尝试从ch
接收,直到内部的for
循环退出。
解决方法是每次更新计数器时都检查ch
。这是我修改后的counter()
函数:
func counter(startFrom int, multiplyBy int, ch <-chan bool) {
for {
for x := startFrom; x < 100; x++ {
time.Sleep(time.Millisecond * 1000)
select {
case <-ch:
return
default:
result := x * multiplyBy
fmt.Printf("%d", result)
}
}
}
}
注意,select
指令嵌套在内部循环中,而不是外部循环中。
另外要指出的一件事是,虽然你的代码在Linux或Mac上可以工作,但在Windows上不行,因为换行符不同。在Windows上,你会得到text=="start\r"
而不是text=="start"
。一个简单的解决方法是text = strings.TrimSpace(text)
。
英文:
You're guessing it right. Once your program hits the default
branch, it will not try receiving from ch
until the inner for
exits.
The solution is to check ch
every time you update the counter. Here is my modified counter()
function:
func counter(startFrom int, multiplyBy int, ch <-chan bool) {
for {
for x := startFrom; x < 100; x++ {
time.Sleep(time.Millisecond * 1000)
select {
case <-ch:
return
default:
result := x * multiplyBy
fmt.Printf("%d", result)
}
}
}
}
Note how the select
directive is nested in the inner loop instead of the outer one.
P.S. One more thing to point out is, while your code will work on Linux or Mac, it won't on Windows because of different line endings. You will get text=="start\r"
instead of text=="start"
on Windows. A simple solution is text = strings.TrimSpace(text)
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论