英文:
How to stop goroutine
问题
我有一个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
}
我该如何停止我的routine?
英文:
I have a goroutine that calls a function and with a special parameter i want to start or stop this goroutine. My problem is that this code never stops my goroutine, it creates everytime a new job.
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
}
How do I stop my routine?
答案1
得分: 1
当你关闭run
通道时,case <-run
将始终触发:监听关闭的通道会立即返回零值。
如果你想停止goroutine,在接收到<-quit
信号后应该返回。
另外,你的default:
子句使for循环主动工作,你应该将其删除(你仍然会监听两个通道)。
英文:
When you close the run
channel, case <-run
will always trigger: listening on a closed channel returns a zero value immediately.
if you want to stop the goroutine, you should return after you get the <-quit
signal.
As a side note, your default:
clause makes the for loop actively work, you should get rid of it (you will still be listening on both channels)
答案2
得分: 1
以下是一个如何实现这样一个信号系统的独立注释可运行版本的示例代码:
package main
import (
"time"
"log"
)
func main() {
statusChannel := make(chan bool)
go applicationLoop(statusChannel)
// 用于测试的合理随机结果
if time.Now().Unix() % 2 == 0 {
statusChannel <- true
} else {
statusChannel <- false
}
for {
// 用于测试的忙碌循环
time.Sleep(1000)
}
}
func applicationLoop(statusChannel chan bool) {
defer close(statusChannel)
for {
log.Printf("等待信号...\n")
shouldContinue := <-statusChannel
if !shouldContinue {
log.Print("收到 false,退出...\n")
break
}
// 在这里运行你的代码
// 由于通道没有缓冲区,你应该使用第二个通道返回结果
log.Print("正在工作...\n")
}
}
请注意,当 statusChannel
不在监听值时向其发送值会导致示例代码出错。你可以使用带缓冲区的通道或者一个在 goroutine 回到监听信号状态时向 main
发送信号的通道来解决这个问题。
英文:
Here's an isolated commented runable version of how such a signaling system might be implemented.
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")
}
}
Do note that sending a value to statusChannel
while is is not listening for a value will make the example blow up in your face. Either use a buffered channel or a channel that signals back to main
when the goroutine is back to listening for a signal.
答案3
得分: 1
这个问题有两个部分。
首先,我们需要以某种方式停止子goroutine,即使父goroutine停止,所有子goroutine也应该收到通知并停止——这是一个向下传递但不向上传递的停止信号层次结构。
另一方面,父goroutine需要等待其子goroutine完成。否则,在某些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
,它可以通过一些技巧实现类似的功能。
英文:
This problem has two parts.
First we need to stop child goroutines somehow in a way that even if a parent goroutines stops, all it's children should get notified and stop - a hierarchy of stop signals that goes down but not up.
On the other hand the parent needs to wait for it's children until they are done. Otherwise we would return from a goroutine or even exit from the app before some goroutines are finished properly.
For simplicity we ignore implementing error handling, timeouts and the like.
For handling the first problem we use context.Context
which gives us a nice hierarchy of execution context handling tools and for solving the second problem we use sync.WaitGroup
which allows us to wait for a group of goroutines to complete their tasks. A simple demonstration would be:
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`)
}
}
}
Which gives us some output like:
[ info ] level2
[ info ] level2
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level2
Currently there is no official package that provide a functionality which combines context.Context
and sync.WaitGroup
. The nearest thing is an errgroup
which can resemble this functionality with some hacks.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论