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
}
1
iyear 2021-12-30 09:55:41 +08:00 via Android
写三次,就读了一次当然剩下两个阻塞出不来了
|
2
wangdashuai 2021-12-30 10:04:52 +08:00
写个 woker 池处理,这样能保证 goroutine 数量不随任务增加。
|
3
CEBBCAT 2021-12-30 10:07:44 +08:00 via Android
queryAll 中的协程加上 select
|
4
keepeye 2021-12-30 10:10:31 +08:00
不知道这个例子的想实现什么功能,仅为了修复而修复的话,可以给 chan 加上 buffer 或者写的时候用 select 就不会阻塞了
|
5
vizee 2021-12-30 10:14:00 +08:00
ch := make(chan int, 3)
脑筋急转弯是吧 |
6
gamexg 2021-12-30 10:34:59 +08:00 1
如楼上,建立 3 缓冲区的 chan
或者写的时候检查是否已满。 select { case ch <- query(): default: } |
8
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 **/ ``` |
9
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 **/ ``` |
10
hzzhzzdogee 2021-12-30 12:06:35 +08:00
@xiaoFine #8 因为 100 毫米以后 query()才返回, 你直接打印 runtime.NumGoroutine()当然会不正确. 实际上 goroutine 并没有泄露
|
11
zwpaper 2021-12-30 12:08:55 +08:00 1
@xiaoFine #8 单从解决 Goroutine 泄漏来说,query 里有 sleep ,你得等 query 跑完了再打 Goroutine 数量,就能看到数量只有 1 ,但是确实让人想不明白写 3 次,读 1 次这个逻辑意义是什么
|
12
xiaoFine 2021-12-30 15:27:55 +08:00
@zwpaper 我能找到的最早的出处是这样 https://medium.com/golangspec/goroutine-leak-400063aef468 ,应该就是单纯讨论 goroutine 泄漏的一个 demo ,不过确实学到了
|
13
index90 2021-12-30 19:24:58 +08:00 1
所有 goroutine 都需要有个 ctx 或者类似的“控制线”,并且独立于“数据线”
在业务逻辑结束之前,通过关闭“控制线”来结束所有 goroutine |
14
SorcererXW 2021-12-30 21:27:19 +08:00
写的时候 select 一下或者用 sync.once 包起来保证只写一次 channel
更好的办法是传一个 context 进去,外部 defer 里面执行一下 cancel |
15
gjquoiai 2021-12-31 18:58:44 +08:00
你这个只能叫背压 demo ,并没有东西泄漏
|
16
zinwalin 2022-01-07 16:01:26 +08:00
为啥我能运行起来
|
17
zinwalin 2022-01-07 16:09:33 +08:00
|