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)
}
}
}
}
CodePudding user response:
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)
.