2017-03-05 38 views
4

凝视运行递归函数的goroutine,我想发送一个信号来停止这些递归函数。这是函数(功能并不重要):停止在goroutine中的所有递归函数

func RecursiveFunc(x int, depth int, quit chan bool) int { 

    if depth == 0 { 
     return 1 
    } 

    if quit != nil { 
     select { 
     case <-quit: 
      return 0 
     default: 
     } 
    } 

    total := 0 

    for i := 0; i < x; i++ { 

     y := RecursiveFunc(x, depth - 1, quit) 

     if y > 0 { 
      total += y 
     } 

    } 

    return total 
} 

该函数可以采用做很长一段时间,我想发送的退出信号,之后停止和使用的结果(不管它是什么)。要运行它:

import (
    "fmt" 
    "time" 
    "sync" 
) 

func main() { 

    quit := make(chan bool) 
    wg := &sync.WaitGroup{} 
    result := -1 

    go func() { 
     defer wg.Done() 
     wg.Add(1) 
     result = RecursiveFunc(5, 20, quit) 
    }() 

    time.Sleep(10 * time.Millisecond) 

    close(quit) // Using `quit <- true` doesn't work 

    wg.Wait() 

    fmt.Println(result) 
} 

要停止够程,我使用的信道说quit和关闭它后,程序效果很好,但我不希望真的关闭通道,我想只是发信号quit <- true。但是,quit <- true不起作用,我可能只退出递归的一个实例。

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

+0

在转到我们context.Context创建依赖门廊的层次结构;请参阅http://stackoverflow.com/questions/42516717/how-to-stop-goroutine/42518866#42518866 –

+0

为什么你不想关闭频道?这是你描述的最简单的方法,所以最好知道你有什么其他限制。 – djd

+0

如果您使用单个通道,则会停止应用中的所有goroutines。或者,如果您希望将渠道用于此特定用途,则必须手动管理它们。如果您只需要在应用程序中停止一部分活动goroutines,那么您会怎么做?此外,使用'context.Context'是Go中管理goroutines的惯用模式。 –

回答

4

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

您可以传递一个context.Context对象作为第一个参数,你需要从外部停止功能,并调用相应的cancel函数来发送一个“消除信号”的功能,这将导致的对Done()通道context.Context将被关闭,并且因此被叫功能将在select声明中被通知取消信号。

下面是函数如何处理使用context.Context消除信号:

func RecursiveFunc(ctx context.Context, x int, depth int) int { 

    if depth == 0 { 
     return 1 
    } 

    select { 
    case <-ctx.Done(): 
     return 0 
    default: 
    } 

    total := 0 

    for i := 0; i < x; i++ { 

     y := RecursiveFunc(ctx, x, depth-1) 

     if y > 0 { 
      total += y 
     } 

    } 

    return total 
} 

这里是你如何调用该函数与新签名:

func main() { 

    wg := &sync.WaitGroup{} 
    result := -1 

    ctx, cancel := context.WithCancel(context.Background()) 

    go func() { 
     defer wg.Done() 
     wg.Add(1) 
     result = RecursiveFunc(ctx, 5, 20) 
    }() 

    time.Sleep(10 * time.Millisecond) 

    cancel() 

    wg.Wait() 

    fmt.Println(result) 
} 
+0

首先,您不仅要在'RecursiveFunc'函数的开头检查'<-ctx.Done()',还要在'for'循环内检查'<-ctx.Done()。第二,在你的'main'函数中,你必须在执行goroutine之前调用'wg.Add(1)',这样就不需要精心设计的延迟('time.Sleep(10 * time.Millisecond)')和同样,如果'RecursiveFunc'少于10ms,你的goroutine将永远不会被激活,'wg.Wait()'会立即返回。 –

+0

@KavehShahbazian:我同意你的第二点,但是在'for'里面让'RecursiveFunc'执行并在'ctx.Done'之后退出有什么不对? – deepmax

+0

即使上下文在那个时候被取消,它也只是不停地发送新的goroutine,而不应该这样做。 –

-1

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

var finishIt bool 

func RecursiveFunc(x int, depth int, quit chan bool) int { 
    if finishIt { 
    return 0 
    } 
//other code here 
} 


//some code here, but than we decide to stop it 
finishIt = true 
+0

我认为这是可以的变量是全球性的,而不是出口这个特定的模块。 – vodolaz095