我有一个goroutine,它调用一个方法,并在通道上传递返回的值:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
如何停止这种goroutine?
我有一个goroutine,它调用一个方法,并在通道上传递返回的值:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
如何停止这种goroutine?
Answers:
编辑: 我意识到您的问题是关于将值发送到goroutine中的chan之前,我匆忙写下了这个答案。下面的方法可以与上面建议的其他chan一起使用,或者利用您已经拥有的chan双向的事实,您可以只使用一个...
如果您的goroutine仅用于处理来自chan的项目,则可以使用内置的“ close”和特殊的通道接收表单。
就是说,一旦您完成了chan上的项目发送,就将其关闭。然后在您的goroutine内,您会为接收操作符获得一个额外的参数,该参数显示通道是否已关闭。
这是一个完整的示例(waitgroup用于确保过程继续进行,直到goroutine完成):
package main
import "sync"
func main() {
var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func() {
for {
foo, ok := <- ch
if !ok {
println("done")
wg.Done()
return
}
println(foo)
}
}()
ch <- 1
ch <- 2
ch <- 3
close(ch)
wg.Wait()
}
defer
call来编写wg.Done()
,并使用range ch
循环遍历所有值直到关闭通道。
通常,您向goroutine传递(可能是单独的)信号通道。当您希望goroutine停止时,该信号通道用于将值推入。goroutine定期轮询该频道。一旦检测到信号,便退出。
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
您不能从外面杀死goroutine。您可以发出信号告知goroutine停止使用通道,但是goroutine上没有句柄可以进行任何类型的元管理。Goroutine旨在合作解决问题,因此杀死行为不端的人几乎永远不会是一个适当的回应。如果要隔离以提高鲁棒性,则可能需要一个过程。
通常,您可以在goroutine中创建一个通道并接收停止信号。
在此示例中,有两种创建频道的方法。
渠道
情境。在示例中,我将演示context.WithCancel
第一个演示,使用channel
:
package main
import "fmt"
import "time"
func do_stuff() int {
return 1
}
func main() {
ch := make(chan int, 100)
done := make(chan struct{})
go func() {
for {
select {
case ch <- do_stuff():
case <-done:
close(ch)
return
}
time.Sleep(100 * time.Millisecond)
}
}()
go func() {
time.Sleep(3 * time.Second)
done <- struct{}{}
}()
for i := range ch {
fmt.Println("receive value: ", i)
}
fmt.Println("finish")
}
第二个演示,使用context
:
package main
import (
"context"
"fmt"
"time"
)
func main() {
forever := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
case <-ctx.Done(): // if cancel() execute
forever <- struct{}{}
return
default:
fmt.Println("for loop")
}
time.Sleep(500 * time.Millisecond)
}
}(ctx)
go func() {
time.Sleep(3 * time.Second)
cancel()
}()
<-forever
fmt.Println("finish")
}
我知道这个答案已经被接受了,但我想我会把2cents丢进去。我喜欢使用墓包。从根本上说,这是一个停止的退出通道,但是它做得很好,例如还传递了任何错误。受控制的例程仍然负责检查远程终止信号。Afaik不可能获取goroutine的“ id”并在行为不当(即陷入无限循环)时将其杀死。
这是我测试过的一个简单示例:
package main
import (
"launchpad.net/tomb"
"time"
"fmt"
)
type Proc struct {
Tomb tomb.Tomb
}
func (proc *Proc) Exec() {
defer proc.Tomb.Done() // Must call only once
for {
select {
case <-proc.Tomb.Dying():
return
default:
time.Sleep(300 * time.Millisecond)
fmt.Println("Loop the loop")
}
}
}
func main() {
proc := &Proc{}
go proc.Exec()
time.Sleep(1 * time.Second)
proc.Tomb.Kill(fmt.Errorf("Death from above"))
err := proc.Tomb.Wait() // Will return the error that killed the proc
fmt.Println(err)
}
输出应如下所示:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
goroutine的功能,以防万一内部发生某些事情而引起恐慌?从技术上讲,goroutine在这种情况下会退出,因此我假设它仍将称为延迟proc.Tomb.Done()
...
proc.Tomb.Done()
会在恐慌使程序崩溃之前执行,但是到底是什么呢?主goroutine可能有很小的机会执行某些语句,但无法从另一goroutine的恐慌中恢复,因此程序仍然崩溃。文档说:“当函数F调用紧急情况时,F的执行停止,F中的所有延迟函数都将正常执行,然后F返回其调用方。该过程将继续执行堆栈,直到返回当前goroutine中的所有函数为止,这时程序崩溃了。”
就个人而言,我想在goroutine的频道上使用range:
https://play.golang.org/p/qt48vvDu8cd
戴夫(Dave)为此写了一篇很棒的文章:http : //dave.cheney.net/2013/04/30/curious-channels。