Home > OS >  How can I pause and resume an operation that is running in a goroutine
How can I pause and resume an operation that is running in a goroutine

Time:10-14

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).

  •  Tags:  
  • go
  • Related