[英]How to stop a goroutine
我有一個調用方法的 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 中,你會得到一個額外的參數給接收操作符,顯示通道是否已經關閉。
這是一個完整的示例(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()
}
你不能從外面殺死一個 goroutine。 您可以向 goroutine 發出信號以停止使用通道,但是 goroutine 無法進行任何類型的元管理。 Goroutines 旨在合作解決問題,因此殺死一個行為不端的人幾乎永遠不會是一個適當的回應。 如果您想要隔離以提高健壯性,您可能需要一個過程。
通常,您可以創建一個通道並在 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。我喜歡使用tomb包。 它基本上是一個 suped up 退出頻道,但它也做一些不錯的事情,比如回傳任何錯誤。 受控制的例程仍負責檢查遠程終止信號。 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
正在執行一些與其他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
線程產生一個goroutine
來執行一些工作一段時間(在本例中為 5 秒)。 當時間到期時,它會向goroutine
發送停止信號並等待它,直到goroutine
完全停止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.