我有一个调用函数和特殊参数的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
}
如何停止常规?
答案 0 :(得分:1)
当您关闭run
频道时,case <-run
将始终触发:在已关闭的频道上收听会立即返回零值。
如果你想停止goroutine,你应该在收到<-quit
信号后返回。
作为旁注,你的default:
子句使for循环有效,你应该摆脱它(你仍然会在两个频道上听)
答案 1 :(得分:1)
这是一个孤立的评论可运行版本,可以实现这样一个信号系统。
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
的通道。
答案 2 :(得分:1)
这个问题有两个部分。
首先,我们需要以某种方式阻止儿童goroutines,即使父母goroutines停止,所有它的孩子都应该得到通知并停止 - 停止信号的层次结构会下降但不会上升。
另一方面,父母需要等待他们的孩子,直到他们完成为止。否则我们会在一些goroutine正确完成之前从goroutine甚至退出应用程序返回。
为简单起见,我们忽略了实现错误处理,超时等。
为了处理第一个问题,我们使用context.Context
为我们提供了一个很好的执行上下文处理工具层次结构,为了解决我们使用sync.WaitGroup
的第二个问题,它允许我们等待一组goroutine到完成他们的任务。一个简单的演示将是:
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
,它可以像一些黑客一样具有这个功能。