如何优雅地停止 Go 例程
在 Go 中,goroutines 提供轻量级并发,但优雅地终止它们可能具有挑战性。这个问题解决了向 Goroutine 发送信号以停止其执行的需要。
提供的代码演示了通过将布尔标志 (tooLate) 设置为 true 来尝试停止 Goroutine。然而,这种方法是有问题的,因为从通道读取数据会阻塞 goroutine。
解决方案 1:使用附加通道
该解决方案涉及使用第二个通道(太晚了)传达停止信号。即使 goroutine 没有主动读取该通道,该通道仍保持打开状态。
<code class="go">func main() { tooLate := make(chan struct{}) // Unbuffered channel for stop signal proCh := make(chan string) go func() { for { fmt.Println("working") time.Sleep(1 * time.Second) select { case <-tooLate: fmt.Println("stopped") return case proCh <- "processed": // Non-blocking send default: // Allows the goroutine to continue without blocking } fmt.Println("done here") } }() select { case proc := <-proCh: fmt.Println(proc) case <-time.After(1 * time.Second): fmt.Println("too late") close(tooLate) // Signal to goroutine to stop } time.Sleep(4 * time.Second) fmt.Println("finish\n") }</code>
在此解决方案中,当时间限制到期时,tooLate 通道将关闭,这会导致 goroutine 退出其阻塞 select 语句并返回。
解决方案 2:使用sync.Cond
或者,您可以使用sync.Cond 类型来实现更复杂的信号机制。下面是一个示例:
<code class="go">func main() { var stopped bool cond := sync.NewCond(new(sync.Mutex)) go func() { for { cond.L.Lock() defer cond.L.Unlock() if stopped { fmt.Println("stopped") return } fmt.Println("working") cond.Wait() // Wait for the signal to stop } }() time.Sleep(1 * time.Second) cond.Signal() // Send the stop signal time.Sleep(4 * time.Second) fmt.Println("finish\n") }</code>
通过这种方法,goroutine 会等待 cond.Wait() 方法,直到收到来自 cond.Signal() 的信号。
以上是如何优雅地停止 Go 例程:避免死锁和无响应进程的指南?的详细内容。更多信息请关注PHP中文网其他相关文章!