这个 goroutine 泄露的 demo 如何修复?

2021-12-30 09:43:31 +08:00
 xoxo419
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
}
2995 次点击
所在节点    Go 编程语言
18 条回复
iyear
2021-12-30 09:55:41 +08:00
写三次,就读了一次当然剩下两个阻塞出不来了
wangdashuai
2021-12-30 10:04:52 +08:00
写个 woker 池处理,这样能保证 goroutine 数量不随任务增加。
CEBBCAT
2021-12-30 10:07:44 +08:00
queryAll 中的协程加上 select
keepeye
2021-12-30 10:10:31 +08:00
不知道这个例子的想实现什么功能,仅为了修复而修复的话,可以给 chan 加上 buffer 或者写的时候用 select 就不会阻塞了
vizee
2021-12-30 10:14:00 +08:00
ch := make(chan int, 3)

脑筋急转弯是吧
gamexg
2021-12-30 10:34:59 +08:00
如楼上,建立 3 缓冲区的 chan

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

select {
case ch <- query():
default:
}
gamexg
2021-12-30 10:43:12 +08:00
@gamexg #6 select 也要保证 chan 至少有 1 的缓冲区
xiaoFine
2021-12-30 11:01:04 +08:00
小白一问,试了下诸君的方法,并不行啊
1. buffer ch
```
func queryAll() int {
ch := make(chan int, 3)

for i := 0; i < 3; i++ {
go func() { ch <- query() }()
}
return <-ch
}
/**
goroutines: 3
goroutines: 5
goroutines: 5
goroutines: 7
**/
```
2. select
```
func queryAll() int {
ch := make(chan int, 3)

for i := 0; i < 3; i++ {
go func() {
select {
case ch <- query():
default:

}
}()
}
return <-ch
}
/**
goroutines: 3
goroutines: 5
goroutines: 5
goroutines: 7
**/
```
xiaoFine
2021-12-30 11:26:38 +08:00
目前能想到的只能是这样(不改变签名),有更优雅的方法吗。。
```
func queryAll() int {
ch := make(chan int)

for i := 0; i < 3; i++ {
go func() {ch <- query()}()
}
<-ch
<-ch
return <-ch
}
/**
goroutines: 1
goroutines: 1
goroutines: 1
goroutines: 1
**/
```
hzzhzzdogee
2021-12-30 12:06:35 +08:00
@xiaoFine #8 因为 100 毫米以后 query()才返回, 你直接打印 runtime.NumGoroutine()当然会不正确. 实际上 goroutine 并没有泄露
zwpaper
2021-12-30 12:08:55 +08:00
@xiaoFine #8 单从解决 Goroutine 泄漏来说,query 里有 sleep ,你得等 query 跑完了再打 Goroutine 数量,就能看到数量只有 1 ,但是确实让人想不明白写 3 次,读 1 次这个逻辑意义是什么
xiaoFine
2021-12-30 15:27:55 +08:00
@zwpaper 我能找到的最早的出处是这样 https://medium.com/golangspec/goroutine-leak-400063aef468 ,应该就是单纯讨论 goroutine 泄漏的一个 demo ,不过确实学到了
index90
2021-12-30 19:24:58 +08:00
所有 goroutine 都需要有个 ctx 或者类似的“控制线”,并且独立于“数据线”
在业务逻辑结束之前,通过关闭“控制线”来结束所有 goroutine
SorcererXW
2021-12-30 21:27:19 +08:00
写的时候 select 一下或者用 sync.once 包起来保证只写一次 channel
更好的办法是传一个 context 进去,外部 defer 里面执行一下 cancel
gjquoiai
2021-12-31 18:58:44 +08:00
你这个只能叫背压 demo ,并没有东西泄漏
zinwalin
2022-01-07 16:01:26 +08:00
为啥我能运行起来
zinwalin
2022-01-07 16:09:33 +08:00
xoxo419
2022-01-10 06:51:32 +08:00
@zinwalin 是可以运行的,泄露只是程序运行的越久占用内存就会越高 最后导致程序无响应

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/825205

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX