我有一个调用方法的 goroutine,并在通道上传递返回值:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
我如何停止这样的 goroutine?
通常,您向 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 中的 chan 发送值之前,我匆忙写下了这个答案。下面的方法可以与上面建议的附加 chan 一起使用,或者使用您已经拥有的 chan 是双向的这一事实,您可以只使用一个......
如果您的 goroutine 仅用于处理来自 chan 的项目,您可以使用“close”内置和通道的特殊接收表单。
也就是说,一旦你在 chan 上发送完项目,你就关闭它。然后在你的 goroutine 中,你会得到一个额外的参数给接收操作符,它显示通道是否已经关闭。
这是一个完整的示例(等待组用于确保进程继续进行,直到 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()
}
通常,您可以在 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")
}
你不能从外面杀死一个 goroutine。您可以向 goroutine 发出信号以停止使用通道,但 goroutine 无法进行任何类型的元管理。Goroutines 旨在合作解决问题,因此杀死一个行为不端的程序几乎永远不会是一个适当的响应。如果您想要隔离以提高健壮性,您可能需要一个过程。
我知道这个答案已经被接受了,但我想我会把我的 2cents 扔进去。我喜欢使用tomb包。它基本上是一个升级的退出通道,但它也可以做一些不错的事情,比如传回任何错误。受控制的例程仍然负责检查远程终止信号。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
就个人而言,我想在 goroutine 的通道上使用范围:
https://play.golang.org/p/qt48vvDu8cd
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
c := make(chan bool)
wg.Add(1)
go func() {
defer wg.Done()
for b := range c {
fmt.Printf("Hello %t\n", b)
}
}()
c <- true
c <- true
close(c)
wg.Wait()
}
戴夫为此写了一篇很棒的文章:http ://dave.cheney.net/2013/04/30/curious-channels 。
我将提供一种与此处提供的方法略有不同的方法。
我将假设goroutine
需要停止的是执行一些与其他完全无关的工作goroutines
。这项工作将由default select case
:
default:
fmt.Println("working")
time.Sleep(1 * time.Second)
另一个goroutine
(在我的示例中是main
)决定它应该停止goroutine
正在执行某些工作的 。你不能真正杀死goroutine
. 即使您可以,这也是一个坏主意,因为它可能会使您goroutine
处于不希望的状态。因此,我们必须使用一个通道来传达有人正在goroutine
向停止发出信号。
stop := make(chan struct{})
由于goroutine
将不断执行一些工作。我们将使用一个循环来表示它。并且当停止信号发出时,就goroutine
跳出循环。
go func() {
L:
for {
select {
case <-stop:
fmt.Println("stopping")
break L
default:
fmt.Println("working")
time.Sleep(1 * time.Second)
}
}
}()
我们可以使用另一个通道来指示main
goroutine 已经停止。这是完整的示例:
package main
import (
"fmt"
"time"
)
func main() {
stop := make(chan struct{})
stopped := make(chan struct{})
go func() {
L:
for {
select {
case <-stop:
fmt.Println("stopping")
break L
default:
fmt.Println("working")
time.Sleep(1 * time.Second)
}
}
fmt.Println("stopped")
stopped <- struct{}{}
}()
<-time.After(5 * time.Second)
stop <- struct{}{} // send a signal to stop
close(stop)
<-stopped // wait for stop
}
main
线程产生 agoroutine
以执行一些工作一段时间(在本例中为 5 秒)。当时间到期时,它会向 发送停止信号goroutine
并等待它,直到goroutine
完全停止。