停止在goroutine中的所有递归函数。

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

Stop all recursive functions in a goroutine

问题

启动一个运行递归函数的goroutine,我想发送一个信号来停止这些递归函数。这是函数(功能不重要):

  1. func RecursiveFunc(x int, depth int, quit chan bool) int {
  2. if depth == 0 {
  3. return 1
  4. }
  5. if quit != nil {
  6. select {
  7. case <-quit:
  8. return 0
  9. default:
  10. }
  11. }
  12. total := 0
  13. for i := 0; i < x; i++ {
  14. y := RecursiveFunc(x, depth - 1, quit)
  15. if y > 0 {
  16. total += y
  17. }
  18. }
  19. return total
  20. }

这个函数可能需要很长时间才能完成,我想在发送退出信号后停止它,并使用结果(无论是什么)。运行它的代码如下:

  1. import (
  2. "fmt"
  3. "time"
  4. "sync"
  5. )
  6. func main() {
  7. quit := make(chan bool)
  8. wg := &sync.WaitGroup{}
  9. result := -1
  10. go func() {
  11. defer wg.Done()
  12. wg.Add(1)
  13. result = RecursiveFunc(5, 20, quit)
  14. }()
  15. time.Sleep(10 * time.Millisecond)
  16. close(quit) // 使用 `quit <- true` 不起作用
  17. wg.Wait()
  18. fmt.Println(result)
  19. }

为了停止goroutine,我使用了一个名为quit的通道,关闭它后程序可以正常工作,但是我不想真正关闭通道,我只想发送一个信号quit <- true。然而,quit <- true不起作用,可能只会停止一个递归实例。

如何通过发送退出信号来停止所有递归函数的实例?

英文:

Staring a goroutine which runs a recursive function, I want to send a signal to stop those recursive functions. This is the function (the functionality is not important):

  1. func RecursiveFunc(x int, depth int, quit chan bool) int {
  2. if depth == 0 {
  3. return 1
  4. }
  5. if quit != nil {
  6. select {
  7. case &lt;-quit:
  8. return 0
  9. default:
  10. }
  11. }
  12. total := 0
  13. for i := 0; i &lt; x; i++ {
  14. y := RecursiveFunc(x, depth - 1, quit)
  15. if y &gt; 0 {
  16. total += y
  17. }
  18. }
  19. return total
  20. }

This function may take a long time to be done and I want stop it after sending a quit signal and use the result (whatever it is). To run it:

  1. import (
  2. &quot;fmt&quot;
  3. &quot;time&quot;
  4. &quot;sync&quot;
  5. )
  6. func main() {
  7. quit := make(chan bool)
  8. wg := &amp;sync.WaitGroup{}
  9. result := -1
  10. go func() {
  11. defer wg.Done()
  12. wg.Add(1)
  13. result = RecursiveFunc(5, 20, quit)
  14. }()
  15. time.Sleep(10 * time.Millisecond)
  16. close(quit) // Using `quit &lt;- true` doesn&#39;t work
  17. wg.Wait()
  18. fmt.Println(result)
  19. }

To stop the goroutine, I'm using a channel say quit and after closing it, the program works well, however I don't want really close the channel and I want just send a signal quit &lt;- true. However, quit &lt;- true doesn't work and I probably quits only one instance of recursion.

How can I stop all instances of recursive function by sending a quit signal?

答案1

得分: 6

你可以使用context来完成你要做的事情。

你可以将一个context.Context对象作为函数的第一个参数传递进去,这样你就可以从外部停止该函数,并调用相应的cancel函数向该函数发送一个“取消信号”。这将导致context.ContextDone()通道关闭,并且被调用的函数将在select语句中收到取消信号的通知。

以下是函数如何使用context.Context处理取消信号的示例代码:

  1. func RecursiveFunc(ctx context.Context, x int, depth int) int {
  2. if depth == 0 {
  3. return 1
  4. }
  5. select {
  6. case <-ctx.Done():
  7. return 0
  8. default:
  9. }
  10. total := 0
  11. for i := 0; i < x; i++ {
  12. y := RecursiveFunc(ctx, x, depth-1)
  13. if y > 0 {
  14. total += y
  15. }
  16. }
  17. return total
  18. }

以下是如何使用新签名调用该函数的示例代码:

  1. func main() {
  2. wg := &sync.WaitGroup{}
  3. result := -1
  4. ctx, cancel := context.WithCancel(context.Background())
  5. go func() {
  6. defer wg.Done()
  7. wg.Add(1)
  8. result = RecursiveFunc(ctx, 5, 20)
  9. }()
  10. time.Sleep(10 * time.Millisecond)
  11. cancel()
  12. wg.Wait()
  13. fmt.Println(result)
  14. }
英文:

You can do the what you are going to do using context.

You can pass a context.Context object as the first parameter to the function which you need to stop from outside, and call the corresponding cancel function to send a "cancellation signal" to the function, which will cause the Done() channel of the context.Context to be closed, and the called function will thus be notified of the cancellation signal in a select statement.

Here is how the function handles the cancellation signal using context.Context:

  1. func RecursiveFunc(ctx context.Context, x int, depth int) int {
  2. if depth == 0 {
  3. return 1
  4. }
  5. select {
  6. case &lt;-ctx.Done():
  7. return 0
  8. default:
  9. }
  10. total := 0
  11. for i := 0; i &lt; x; i++ {
  12. y := RecursiveFunc(ctx, x, depth-1)
  13. if y &gt; 0 {
  14. total += y
  15. }
  16. }
  17. return total
  18. }

And here is how you can call the function with the new signature:

  1. func main() {
  2. wg := &amp;sync.WaitGroup{}
  3. result := -1
  4. ctx, cancel := context.WithCancel(context.Background())
  5. go func() {
  6. defer wg.Done()
  7. wg.Add(1)
  8. result = RecursiveFunc(ctx, 5, 20)
  9. }()
  10. time.Sleep(10 * time.Millisecond)
  11. cancel()
  12. wg.Wait()
  13. fmt.Println(result)
  14. }

答案2

得分: 1

我最近也遇到了类似的情况,就像你的情况一样,退出信号被递归分支中的一个消耗掉,导致其他分支没有信号。我通过在函数返回之前将停止信号转发到通道来解决这个问题。

例如,你可以修改递归函数中的 select 语句如下:

  1. if quit != nil {
  2. select {
  3. case <-quit:
  4. quit <- true // 转发信号
  5. return 0
  6. default:
  7. }
  8. }
英文:

I was in a similar situation recently and like your case, the quit signal was consumed by one of the recursion branch leaving the other branches without a signal. I solved this by forwarding the stop signal to the channel before returning from the function.

For example, you can modify the select inside the recursive function to be:

  1. if quit != nil {
  2. select {
  3. case &lt;-quit:
  4. quit &lt;- true // forward the signal
  5. return 0
  6. default:
  7. }
  8. }

答案3

得分: 0

  1. // 递归函数无限循环直到条件 >= 10 成立,不要忘记关闭通道并返回
  2. func main() {
  3. x := 1
  4. xChan := make(chan int)
  5. go recursion(x, xChan)
  6. select {
  7. case result := <-xChan:
  8. log.Println("获取通道结果:", result)
  9. break
  10. }
  11. }
  12. func recursion(i int, xChan chan int) {
  13. if i >= 10 {
  14. xChan <- i
  15. close(xChan)
  16. return
  17. }
  18. a := i + i
  19. log.Println("a:", a)
  20. recursion(a, xChan)
  21. }
英文:

function recursion loop infinitely util the condition >= 10 matchs, don't forget to close channel and return

  1. func main() {
  2. x := 1
  3. xChan := make(chan int)
  4. go recursion(x, xChan)
  5. select {
  6. case result := &lt;-xChan:
  7. log.Println(&quot;get chan result :&quot;, result)
  8. break
  9. }
  10. }
  11. func recursion(i int, xChan chan int) {
  12. if i &gt;= 10 {
  13. xChan &lt;- i
  14. close(xChan)
  15. return
  16. }
  17. a := i + i
  18. log.Println(&quot;a :&quot;, a)
  19. recursion(a, xChan)
  20. }

答案4

得分: -1

尝试添加标志以继续执行,但可能不是线程安全的。

  1. var finishIt bool
  2. func RecursiveFunc(x int, depth int, quit chan bool) int {
  3. if finishIt {
  4. return 0
  5. }
  6. //其他代码在这里
  7. }
  8. //一些代码在这里,但然后我们决定停止它
  9. finishIt = true
英文:

Try to add flag to continue execution, but it can be not thread safe.

  1. var finishIt bool
  2. func RecursiveFunc(x int, depth int, quit chan bool) int {
  3. if finishIt {
  4. return 0
  5. }
  6. //other code here
  7. }
  8. //some code here, but than we decide to stop it
  9. finishIt = true

huangapple
  • 本文由 发表于 2017年3月5日 16:14:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/42606049.html
匿名

发表评论

匿名网友

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

确定