【问题标题】:How do I kill a goroutine我如何杀死一个 goroutine
【发布时间】:2020-05-04 14:29:31
【问题描述】:

我有以下设置:

func startsMain (){
    go main ()
}

fun stopMain (){
    //kill main
}

func main() {
    //infinite loop 
}

我正在创建黄瓜步骤,我需要能够启动和关闭应用程序。

【问题讨论】:

  • 你不能从“外部”停止一个 goroutine。 goroutine 必须支持某种终止信号(通常是通道)。但如果它没有,你就不能强迫它或杀死它。 cancel a blocking operation in Go 的可能重复项。
  • 你不能简单地杀死一个goroutine;请举例说明您要解决的问题。
  • @icza,是否可以使用任何其他方式而不是 goroutine 来启动和停止 main ?
  • @Michael: main 由运行时在主包中隐式调用。你不能自己称呼它。
  • 我的意思是,如果你想从外部停止 main(),你总是可以调用类似 os.Exit() 的方法。这通过杀死整个应用程序来杀死 main,甚至可以从“main”goroutine 之外调用。如果你想停止主协程但让其他协程保持活动状态,你只能在主协程(但不一定是 main() 函数本身)中使用runtime.Goexit() 执行此操作,尽管这有辅助副作用(就像如果所有其他 goroutine 也退出,应用程序就会崩溃)。

标签: go goroutine


【解决方案1】:

您可以使用select 和频道杀死您的无限循环!

var quit chan struct{}

func startLoop() {
    quit := make(chan struct{})
    go loop()
}

func stopLoop() {
    // As mentioned by Kaedys
    //close(quit)
    // permits signalling everyone havins such a `case <-quit:`
    // statement to be stopped at once, which might be even better.
    quit <- struct{}{}
}

// BTW, you cannot call your function main, it is reserved
func loop() {
    for {
        select {
        case <-quit:
            return # better than break
        default:
            // do stuff. I'd call a function, for clarity:
            do_stuff()
        }
    }
}

很好的 Go 交换,不是吗?

现在,这是什么奇怪的chan struct{}?它是一个零大小的通道。我们只能用空结构填充它(即:struct{}{})。它可以是chan bool 或其他任何内容,因为我们不使用频道的内容。重要的一点是,我们使用quit 通道来通知我们的goroutine 中的无限循环,该停止了。

select 语句用于捕获来自通道的内容。这是一个阻塞语句(将停止执行,直到将某些内容放入case 调查的通道之一),除非您放入default 语句。在这种情况下,每次执行select 时,如果在quit 中放入某些内容,则循环将中断,否则将调用do_stuff()。如果您使用过Go Tour,您已经知道这一点。

其他很酷的并发模式可以在Go Blog上找到。

最后,为了更有趣,您可以使用Tickers 要求您的do_stuff 函数定期执行,而不是像这样消耗100% CPU:

import "time"

// [...]

func loop() {
    // This ticker will put something in its channel every 2s 
    ticker := time.NewTicker(2 * time.Second)
    // If you don't stop it, the ticker will cause memory leaks
    defer ticker.Stop()
    for {
        select {
        case <-quit:
            return
        case <-ticker.C:
            // do stuff. I'd call a function, for clarity:
            do_stuff()
        }
    }
}

这里,select 处于阻塞状态,因为我们删除了 default 语句。

【讨论】:

  • 值得注意的是,退出频道的典型样式是 close() 它们,而不是通过它们发送值。这不仅在代码中更简洁,而且关闭的通道会立即向 所有 侦听器发出信号,否则您必须通过通道为每个侦听器发送单独的消息。另外,main() 是默认调用的程序入口点,您不需要(而且我相信不能)将它与另一个函数分开调用。
  • 我听说close()case 被执行,我认为这不合逻辑。但现在我明白了:同时向每个人发出信号对我来说非常有用,谢谢 :)
  • 更好的是context 包,请参阅golang.org/pkg/context 您应该将示例更改为使用ctx, cancel := context.WithCancel(context.Background())。那么main可以使用defer cancel(),goroutines可以使用ctx.Done()通道上的select退出。
  • nit,没有所谓的“零大小通道”。无论其类型如何,所有通道在内存中的大小都相同。如果这就是你的意思,它可能是无缓冲的。
  • @JimB 我认为他的意思是消息本身,struct{}{},大小为零。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-15
  • 2021-09-11
  • 2019-08-24
  • 1970-01-01
  • 2012-08-15
  • 2018-09-12
  • 1970-01-01
相关资源
最近更新 更多