Golang Go语言中这个 goroutine 泄露的 demo 如何修复?

发布于 1周前 作者 eggper 来自 Go语言

Golang Go语言中这个 goroutine 泄露的 demo 如何修复?

func main() {
for i := 0; i < 4; i++ {
queryAll()
fmt.Printf(“goroutines: %d\n”, runtime.NumGoroutine())
}
}

func queryAll() int { ch := make(chan int) for i := 0; i < 3; i++ { go func() { ch <- query() }() } return <-ch }

func query() int { n := rand.Intn(100) time.Sleep(time.Duration(n) * time.Millisecond) return n }


更多关于Golang Go语言中这个 goroutine 泄露的 demo 如何修复?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

19 回复

写三次,就读了一次当然剩下两个阻塞出不来了

更多关于Golang Go语言中这个 goroutine 泄露的 demo 如何修复?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


写个 woker 池处理,这样能保证 goroutine 数量不随任务增加。

queryAll 中的协程加上 select

不知道这个例子的想实现什么功能,仅为了修复而修复的话,可以给 chan 加上 buffer 或者写的时候用 select 就不会阻塞了

ch := make(chan int, 3)

脑筋急转弯是吧

如楼上,建立 3 缓冲区的 chan

或者写的时候检查是否已满。

select {
case ch <- query():
default:
}

#6 select 也要保证 chan 至少有 1 的缓冲区

小白一问,试了下诸君的方法,并不行啊
1. buffer ch
<br>func queryAll() int {<br> ch := make(chan int, 3)<br><br> for i := 0; i &lt; 3; i++ {<br> go func() { ch &lt;- query() }()<br> }<br> return &lt;-ch<br>}<br>/**<br>goroutines: 3<br>goroutines: 5<br>goroutines: 5<br>goroutines: 7<br>**/<br>
2. select
<br>func queryAll() int {<br> ch := make(chan int, 3)<br><br> for i := 0; i &lt; 3; i++ {<br> go func() {<br> select {<br> case ch &lt;- query():<br> default:<br><br> }<br> }()<br> }<br> return &lt;-ch<br>}<br>/**<br>goroutines: 3<br>goroutines: 5<br>goroutines: 5<br>goroutines: 7<br>**/<br>

目前能想到的只能是这样(不改变签名),有更优雅的方法吗。。
<br>func queryAll() int {<br> ch := make(chan int)<br><br> for i := 0; i &lt; 3; i++ {<br> go func() {ch &lt;- query()}()<br> }<br> &lt;-ch<br> &lt;-ch<br> return &lt;-ch<br>}<br>/**<br>goroutines: 1<br>goroutines: 1<br>goroutines: 1<br>goroutines: 1<br>**/<br>

#8 因为 100 毫米以后 query()才返回, 你直接打印 runtime.NumGoroutine()当然会不正确. 实际上 goroutine 并没有泄露

#8 单从解决 Goroutine 泄漏来说,query 里有 sleep ,你得等 query 跑完了再打 Goroutine 数量,就能看到数量只有 1 ,但是确实让人想不明白写 3 次,读 1 次这个逻辑意义是什么

我能找到的最早的出处是这样 https://medium.com/golangspec/goroutine-leak-400063aef468 ,应该就是单纯讨论 goroutine 泄漏的一个 demo ,不过确实学到了

所有 goroutine 都需要有个 ctx 或者类似的“控制线”,并且独立于“数据线”
在业务逻辑结束之前,通过关闭“控制线”来结束所有 goroutine

写的时候 select 一下或者用 sync.once 包起来保证只写一次 channel
更好的办法是传一个 context 进去,外部 defer 里面执行一下 cancel

你这个只能叫背压 demo ,并没有东西泄漏

为啥我能运行起来

是可以运行的,泄露只是程序运行的越久占用内存就会越高 最后导致程序无响应

在Go语言中,goroutine泄露通常发生在goroutine被意外地创建后没有适当的机制来终止它们。要修复goroutine泄露,你需要确保每个goroutine在完成其任务后都能正确退出,或者在需要的时候能够被外部信号终止。

以下是一些常见的修复goroutine泄露的策略:

  1. 使用上下文(context): 上下文对象可以用来在goroutine之间传递取消信号。你可以创建一个带取消功能的上下文,并在goroutine中监听这个上下文的Done通道。一旦外部请求取消,Done通道会被关闭,goroutine可以据此退出。

  2. 确保通道(channel)的正确关闭: 如果你使用通道在goroutine之间通信,确保在不再需要时正确关闭通道。这可以帮助goroutine识别何时停止工作。不过要注意,关闭发送方通道前要确保没有正在发送的操作,关闭接收方通道前要确保没有正在接收的阻塞操作。

  3. 使用WaitGroup或其他同步机制: sync.WaitGroup可以用来等待一组goroutine完成。在每个goroutine启动时增加一个WaitGroup计数,在goroutine结束时减少计数。主goroutine可以等待直到所有子goroutine完成。

  4. 定期检查和清理: 在某些情况下,可能需要定期检查系统中的goroutine数量,并采取措施清理那些不应该存在的goroutine。这通常涉及到更复杂的逻辑和状态管理。

修复goroutine泄露需要具体分析代码中的泄露点,并根据实际情况选择合适的策略。

回到顶部