129

メソッドを呼び出し、チャネルに戻り値を渡すゴルーチンがあります。

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()

どうすればそのようなゴルーチンを止めることができますか?

4

7 に答える 7

139

通常、ゴルーチンを(場合によっては別の)信号チャネルに渡します。その信号チャネルは、ゴルーチンを停止するときに値をプッシュするために使用されます。ゴルーチンはそのチャネルを定期的にポーリングします。信号を検出するとすぐに終了します。

quit := make(chan bool)
go func() {
    for {
        select {
        case <- quit:
            return
        default:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
quit <- true
于 2011-07-24T15:39:24.593 に答える
65

編集: あなたの質問がゴルーチン内のちゃんに値を送ることについてであることに気付く前に、私は急いでこの答えを書きました。以下のアプローチは、上記のように追加のちゃんと一緒に使用することも、すでに持っているちゃんが双方向であるという事実を使用して、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()
}
于 2011-11-11T19:19:11.730 に答える
45

通常、チャネルを作成して、ゴルーチンで停止信号を受信できます。

この例では、チャネルを作成する2つの方法があります。

  1. チャネル

  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")
}
于 2017-11-15T08:47:06.287 に答える
44

外からゴルーチンを殺すことはできません。チャネルの使用を停止するようにゴルーチンに信号を送ることはできますが、あらゆる種類のメタ管理を行うためのゴルーチンのハンドルはありません。Goroutinesは協力して問題を解決することを目的としているため、誤動作している問題を強制終了しても、適切な対応になることはほとんどありません。堅牢性のために分離が必要な場合は、おそらくプロセスが必要です。

于 2011-07-28T07:40:51.643 に答える
15

この答えはすでに受け入れられていることは知っていますが、私は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
于 2013-11-23T18:34:01.163 に答える
10

個人的には、ゴルーチンのチャネルで範囲を使用したいと思います。

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

于 2014-05-14T06:05:02.123 に答える
0

ここで提供されているものとは少し異なるアプローチを提供します。

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

于 2021-11-08T13:59:50.190 に答える