如何暂停和恢复在goroutine中运行的操作?

huangapple go评论80阅读模式
英文:

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 (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;strings&quot;
	&quot;time&quot;
)

func main() {

	fmt.Println(&quot;starting...&quot;)

	reader := bufio.NewReader(os.Stdin)

	quit := make(chan bool)

	for {
		text, _ := reader.ReadString(&#39;\n&#39;)

		text = strings.Replace(text, &quot;\n&quot;, &quot;&quot;, -1)

		fmt.Printf(&quot;entered: %s\n&quot;, text)

		switch {
		case text == &quot;start&quot;:
			fmt.Println(&quot;starting&quot;)
			go counter(1, 1, quit)
		case text == &quot;pause&quot;:
			fmt.Println(&quot;pausing&quot;)
		case text == &quot;resume&quot;:
			fmt.Println(&quot;resuming&quot;)
		case text == &quot;stop&quot;:
			fmt.Println(&quot;stopping&quot;)
			quit &lt;- true
		}

	}
}

func counter(startFrom int, multiplyBy int, ch &lt;-chan bool) {

	for {
		select {
		case &lt;-ch:
			return
		default:
			for x := startFrom; x &lt; 100; x++ {
				time.Sleep(time.Millisecond * 1000)
				result := x * multiplyBy
				fmt.Printf(&quot;%d&quot;, 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 &lt;-chan bool) {
	for {
		for x := startFrom; x &lt; 100; x++ {
			time.Sleep(time.Millisecond * 1000)
			select {
			case &lt;-ch:
				return
			default:
				result := x * multiplyBy
				fmt.Printf(&quot;%d&quot;, 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==&quot;start\r&quot; instead of text==&quot;start&quot; on Windows. A simple solution is text = strings.TrimSpace(text).

huangapple
  • 本文由 发表于 2022年10月13日 10:48:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/74049965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定