私はメソッドを呼び出し、チャネルに戻り値を渡すゴルーチンを持っています:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
このようなゴルーチンを止めるにはどうすればいいですか?
私はメソッドを呼び出し、チャネルに戻り値を渡すゴルーチンを持っています:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
このようなゴルーチンを止めるにはどうすればいいですか?
回答:
編集: 私はあなたの質問がgoroutine内のchanに値を送信することに関するものであることを理解する前に、急いでこの回答を書きました。以下のアプローチは、上記のように追加のchanで使用するか、すでに持っているchanが双方向であるという事実を使用して、1つだけを使用することができます...
chanから出てくるアイテムを処理するためだけにゴルーチンが存在する場合は、組み込みの "close"ビルトインとチャネル用の特別な受信フォームを利用できます。
つまり、ちゃんにアイテムを送信し終えたら、ちゃんと閉じます。次に、goroutine内で、チャネルが閉じているかどうかを示す追加パラメーターをreceiveオペレーターに取得します。
以下は完全な例です(ウェイトグループは、ゴルーチンが完了するまでプロセスが続行することを確認するために使用されます)。
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
してより慣用的に記述され、チャネルが閉じるまですべての値を反復wg.Done()
するrange ch
ループです。
通常、goroutineに(場合によっては別の)信号チャネルを渡します。その信号チャネルは、ゴルーチンを停止させたいときに値をプッシュするために使用されます。goroutineはそのチャネルを定期的にポーリングします。シグナルを検出するとすぐに終了します。
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
外からゴルーチンを殺すことはできません。チャネルの使用を停止するようにゴルーチンに信号を送ることはできますが、ゴルーチンにはなんらかのメタ管理を行うためのハンドルがありません。ゴルーチンは協調的に問題を解決することを目的としているため、動作に問題のあるものを殺しても、適切な対応はほとんどありません。堅牢性のための分離が必要な場合は、おそらくプロセスが必要です。
一般に、チャネルを作成して、goroutineで停止信号を受信できます。
この例では、チャネルを作成する方法が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")
}
私はこの答えは、すでに承認されているけど、私は私は私の2centsを投げると思った。私が使用したい墓のパッケージを。これは基本的には終了したquitチャネルですが、エラーを返すなどの素晴らしいことも行います。制御下にあるルーチンは、リモートkillシグナルをチェックする責任があります。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
tomb
たとえば、パニックをスローするような何かが内部で発生した場合に備えて、ゴルーチンをどうするかをテストしましたか?技術的に言えば、この場合、ゴルーチンは終了するので、それがまだ遅延オブジェクトを呼び出すと想定していますproc.Tomb.Done()
...
proc.Tomb.Done()
、パニックがプログラムをクラッシュさせる前に実行されますが、何のために?メインのゴルーチンには、いくつかのステートメントを実行する非常に小さな機会がある可能性がありますが、別のゴルーチンのパニックから回復する方法がないため、プログラムは引き続きクラッシュします。ドキュメントは言う:「関数Fがパニックを呼び出すと、Fの実行が停止し、Fのすべての遅延関数が正常に実行され、Fは呼び出し元に戻ります。プロセスは、現在のゴルーチンのすべての関数が戻るまでスタックを続けます。その時点でプログラムはクラッシュします。」
個人的に、私はゴルーチンのチャンネルで範囲を使用したいと思います:
https://play.golang.org/p/qt48vvDu8cd
Daveはこれについて素晴らしい投稿を書きました:http : //dave.cheney.net/2013/04/30/curious-channels。