如何阻止goroutine

Łuk*_*ner 90 go goroutine channels

我有一个调用方法的goroutine,并在通道上传递返回值:

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()
Run Code Online (Sandbox Code Playgroud)

我怎么停止这样的goroutine?

jim*_*imt 104

通常,您传递goroutine(可能是单独的)信号通道.当您希望goroutine停止时,该信号通道用于将值推入.goroutine定期进行民意调查.一旦检测到信号,它就会退出.

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

// Do stuff

// Quit goroutine
quit <- true
Run Code Online (Sandbox Code Playgroud)

  • 然后应该修复bug. (204认同)
  • 还不够好.如果由于一个错误,goroutine卡在无限循环中怎么办? (25认同)
  • Elazar,你建议的是一种在你调用它之后停止某个功能的方法.goroutine不是一个主题.它可以在不同的线程中运行,也可以在与您相同的线程中运行.我知道没有任何语言支持你认为Go应该支持的东西. (12认同)
  • 多任务处理是合作的,而不是先发制人的.循环中的goroutine永远不会进入调度程序,因此永远不会被杀死. (10认同)
  • @jeremy不同意Go,但是Erlang允许你杀死一个运行循环函数的进程. (4认同)
  • 你最好关闭通道,这样任何监听器(不仅是第一个从`quit`接收)都会收到`quit`信号. (3认同)
  • 如果您决定关闭频道至少有一个区别:您可能需要在main()结束前给goroutine时间完成"Do other stuff".我创建了一个演示这个的游乐场:https://play.golang.org/p/EdL3fdxoBp (3认同)
  • @jimt 我希望我能对你的评论投反对票(尽管对有用的答案 +1)。Elazar 所说的有一些优点,尽管它可能措辞错误:我们如何停止一个忙碌的 goroutine,这就是问题所在。当可以完美地解释 Go 的并发模型时,为什么要摆脱某人,Jeremy 的回答对我来说非常清楚。当对方要求解释时,不需要“有魅力”的回答。 (3认同)
  • 我真的不敢相信@jimt 的评论得到了这么多的支持。在很多情况下,此错误可能会发生在外部库中,因此我想确保运行此外部方法的 go 例程可以在某个时刻停止,并且不会使用更多资源。 (2认同)

las*_*owh 47

编辑: 我在匆忙中写下这个答案,然后才意识到你的问题是关于将值发送给goroutine中的chan.下面的方法可以使用上面建议的附加陈,或使用你已经是双向的陈的事实,你可以只使用一个......

如果您的goroutine仅用于处理来自chan的项目,您可以使用"close"内置和频道的特殊接收表单.

也就是说,一旦你完成在陈上发送项目,你就关闭它.然后在你的goroutine里面你会得到一个额外的参数给接收运算符,它显示了通道是否已经关闭.

这是一个完整的示例(等待组用于确保过程继续,直到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()
}
Run Code Online (Sandbox Code Playgroud)

  • 内部goroutine的主体使用`defer`来调用`wg.Done()`,以及`range ch`循环来迭代所有值,直到通道关闭为止. (14认同)

Ste*_*ark 28

你不能从外面杀死goroutine.你可以发信号通知goroutine停止使用频道,但是goroutines没有办法进行任何类型的元管理.Goroutines旨在合作解决问题,因此杀死一个行为不端的人几乎永远不会是一个充分的反应.如果您希望隔离稳健性,那么您可能需要一个进程.


zou*_*ing 11

通常,您可以在go例程中创建一个通道并接收停止信号.

在此示例中有两种创建通道的方法.

  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")
}
Run Code Online (Sandbox Code Playgroud)

第二个演示,使用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")
}
Run Code Online (Sandbox Code Playgroud)

  • 这正是我正在寻找的! (3认同)

Kev*_*ell 8

我知道这个答案已经被接受了,但我想我会把我的两个人扔掉.我喜欢使用包.它基本上是一个suped up quit频道,但它做了很好的事情,比如传回任何错误.受控制的程序仍然负责检查远程终止信号.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)
}
Run Code Online (Sandbox Code Playgroud)

输出应如下所示:

# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Run Code Online (Sandbox Code Playgroud)


mik*_*ook 5

就个人而言,我想在goroutine的频道上使用范围:

http://play.golang.org/p/KjG8FLzPoz

戴夫写了一篇很棒的帖子:http://dave.cheney.net/2013/04/30/curious-channels.

  • 那真是太美了。 (2认同)