我有一个 goroutine,它调用一个方法,并在一个通道上传递返回值:
ch := make(chan int, 100) go func(){ for { ch <- do_stuff() } }()
我怎么才能阻止这样的事情发生呢?
通常,您将一个(可能是单独的)信号通道传递给 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 没有处理任何类型的元管理。Goroutines 的目的是合作解决问题,所以杀死一个行为不端的人几乎永远不会是一个适当的反应。如果需要为了健壮性而进行隔离,则可能需要一个进程。
编辑: < em > 在意识到你的问题是关于在 goroutine 中向 chan 发送值之前,我匆忙地写下了这个答案。下面的方法可以与上面建议的额外的 chan 一起使用,或者使用你已经有的 chan 是双向的这一事实,你可以只使用一个..。
如果您的 goroutine 存在的唯一目的是处理出来的项目禅,您可以利用“关闭”内置和通道的特殊接收形式。
也就是说,一旦您完成了在 chan 上发送项目,就关闭它。然后,在 goroutine 中,接收操作符获得一个额外的参数,用于显示通道是否已关闭。
下面是一个完整的示例(使用 wait 组确保流程继续下去,直到 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() }
我知道这个答案已经被接受了,但是我想我应该加入进来。我喜欢使用 坟墓软件包。它基本上是一个假想的退出通道,但它也可以做很多事情,比如传回任何错误。受控的例程仍然负责检查远程终止信号。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 频道中使用 range:
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() }
Dave 写了一篇很棒的文章: http://dave.cheney.net/2013/04/30/curious-channels。
通常,您可以在 goroutine 中创建一个通道并接收一个停止信号。
在这个例子中有两种创建通道的方法。
频道
在这个例子中,我将演示 context.WithCancel
context.WithCancel
第一个演示,使用 channel:
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:
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正在执行一些与其他 goroutines完全无关的工作。这项工作将由 default select case代表:
goroutine
goroutines
default select case
default: fmt.Println("working") time.Sleep(1 * time.Second)
另一个 goroutine(在我的例子中是 main)决定它应该停止正在执行某些工作的 goroutine。你不能真的杀死 goroutine。即使你可以这将是一个坏主意,因为它可以离开 goroutine在一个不想要的状态。因此,我们必须使用一个信道来通信,有人正在向 goroutine发出停止的信号。
main
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线程产生一个 goroutine来执行某些工作一段时间(在本例中为5秒)。当时间到期时,它向 goroutine发送一个停止信号并等待它,直到 goroutine完全停止。
我修改了邹英的答案,做了一个可停止的计时器,希望能帮到别人。
func main() { timeOut := make(chan bool) stopTimer := false //10 sec timer - go-routine go func() { var counter = 0 for { if stopTimer{ timeOut <- false return }else { fmt.Printf("timer: %d sec\n", counter) time.Sleep(1 * time.Second) counter++ if counter >= 10 { timeOut <- true return } } } }() go func() { time.Sleep(8 * time.Second) //change 8 to 11 or more to time-out the timer stopTimer = true }() b := <-timeOut fmt.Printf("time-out ,%v\n", b) }