我有一个goroutine,它调用一个函数,并使用一个特殊的参数来启动或停止这个goroutine。我的问题是,这段代码从来不会停止我的goroutine,它会在每次新作业时创建。
quit := make(chan bool)
run := make(chan bool)
go func() {
for {
select {
case <-quit:
close(run)
case <-run:
myFunc(c)
default:
}
}
}()
if x == true {
quit <- true
} else {
run <- true
}我如何停止我的例程?
发布于 2017-03-01 03:08:09
当您关闭run通道时,case <-run将始终触发:在关闭的通道上侦听将立即返回零值。
如果你想停止goroutine,你应该在收到<-quit信号后返回。
顺便说一句,你的default:子句让for循环积极地工作,你应该去掉它(你仍然会在两个通道上监听)
发布于 2017-03-01 03:22:03
下面是一个孤立的、带注释的、可运行的版本,说明如何实现这样的信令系统。
package main
import (
"time"
"log"
)
func main() {
statusChannel := make(chan bool)
go applicationLoop(statusChannel)
// reasonably random outcome for testing
if time.Now().Unix() % 2 == 0 {
statusChannel<-true
} else {
statusChannel<-false
}
for {
// busy loop for testing
time.Sleep(1000)
}
}
func applicationLoop(statusChannel chan bool) {
defer close(statusChannel)
for {
log.Printf("waiting for signal...\n")
shouldContinue := <-statusChannel
if !shouldContinue {
log.Print("received false, breaking...\n")
break
}
// run your code here
// you should use a second channel to return results, as the channel is not buffered
log.Print("working...\n")
}
}请注意,向statusChannel发送一个值,而不是侦听一个值,这将使示例在您的面前爆炸。要么使用缓冲通道,要么使用在goroutine重新侦听信号时向main发回信号的通道。
发布于 2017-03-01 05:15:35
这个问题有两个部分。
首先,我们需要以某种方式停止子goroutines,即使父goroutines停止,它的所有子进程也应该得到通知并停止--一个向下但不向上的停止信号的层次结构。
另一方面,父母需要等待它的孩子,直到他们完成。否则,我们将从goroutine返回,甚至在某些goroutine正确完成之前退出应用程序。
为了简单起见,我们忽略了实现错误处理、超时等。
为了处理第一个问题,我们使用了context.Context,它为我们提供了一个很好的执行上下文处理工具的层次结构;为了解决第二个问题,我们使用了sync.WaitGroup,它允许我们等待一组goroutines来完成它们的任务。一个简单的演示是:
func main() {
all := &sync.WaitGroup{}
rootCtx, rootCancel := context.WithCancel(context.Background())
all.Add(1)
go level1(rootCtx, all)
// just to simulate stop, we could use an os signal instead
// app ends after 3 seconds
go func() {
time.Sleep(time.Second * 3)
rootCancel()
}()
all.Wait()
}
func level1(parent context.Context, all *sync.WaitGroup) {
defer all.Done()
l1Ctx, l1Cancel := context.WithCancel(parent)
defer l1Cancel()
for i := 0; i < 3; i++ {
all.Add(1)
go level2(l1Ctx, all)
}
for {
select {
case <-parent.Done():
return
// other cases if any,
// this is a sample
case <-time.After(time.Second):
log.Println(`level1`)
}
}
}
func level2(parent context.Context, all *sync.WaitGroup) {
defer all.Done()
for {
select {
case <-parent.Done():
return
case <-time.After(time.Second):
log.Println(`level2`)
}
}
}这给了我们一些输出,比如:
[ info ] level2
[ info ] level2
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level2目前还没有提供结合context.Context和sync.WaitGroup功能的官方包。最近的东西是一个errgroup,它可以通过一些hack类似于这个功能。
https://stackoverflow.com/questions/42516717
复制相似问题