メソッドを呼び出し、チャネルに戻り値を渡すゴルーチンがあります。
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
どうすればそのようなゴルーチンを止めることができますか?
通常、ゴルーチンを(場合によっては別の)信号チャネルに渡します。その信号チャネルは、ゴルーチンを停止するときに値をプッシュするために使用されます。ゴルーチンはそのチャネルを定期的にポーリングします。信号を検出するとすぐに終了します。
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
編集: あなたの質問がゴルーチン内のちゃんに値を送ることについてであることに気付く前に、私は急いでこの答えを書きました。以下のアプローチは、上記のように追加のちゃんと一緒に使用することも、すでに持っているちゃんが双方向であるという事実を使用して、1つだけを使用することもできます...
ちゃんから出てくるアイテムを処理するためだけにゴルーチンが存在する場合は、「閉じる」ビルトインとチャンネル用の特別な受信フォームを利用できます。
つまり、ちゃんにアイテムを送信し終えたら、それを閉じます。次に、ゴルーチン内で、チャネルが閉じられているかどうかを示す追加のパラメーターを受信オペレーターに取得します。
完全な例を次に示します(待機グループは、ゴルーチンが完了するまでプロセスが継続することを確認するために使用されます)。
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()
}
通常、チャネルを作成して、ゴルーチンで停止信号を受信できます。
この例では、チャネルを作成する2つの方法があります。
チャネル
コンテキスト。例では、デモを行います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")
}
2番目のデモ、使用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")
}
外からゴルーチンを殺すことはできません。チャネルの使用を停止するようにゴルーチンに信号を送ることはできますが、あらゆる種類のメタ管理を行うためのゴルーチンのハンドルはありません。Goroutinesは協力して問題を解決することを目的としているため、誤動作している問題を強制終了しても、適切な対応になることはほとんどありません。堅牢性のために分離が必要な場合は、おそらくプロセスが必要です。
この答えはすでに受け入れられていることは知っていますが、私は2セントを投入すると思いました。私は墓のパッケージを使用するのが好きです。これは基本的には終了チャネルですが、エラーを返すなどの優れた機能も備えています。制御下にあるルーチンには、リモートキル信号をチェックする責任があります。Afaikは、ゴルーチンの「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
個人的には、ゴルーチンのチャネルで範囲を使用したいと思います。
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
て、ゴルーチンが停止したことを示すことができます。完全な例は次のとおりです。
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
スレッドは、しばらくの間(この場合は5秒)作業を実行するためにaを生成しgoroutine
ます。時間が経過すると、に停止信号を送信し、が完全に停止goroutine
するまで待機します。goroutine