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 ![]() 如楼上,建立 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 ikw 2021-12-30 12:08:55 +08:00 ![]() @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 ![]() 所有 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 |