V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xoxo419
V2EX  ›  Go 编程语言

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

  •  
  •   xoxo419 · 2021-12-30 09:43:31 +08:00 · 3153 次点击
    这是一个创建于 1071 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 条附言  ·  2021-12-30 19:31:28 +08:00
    只是单纯讨论 goroutine 泄漏的一个 demo ,想从这个 demo 去学习如何分析泄露和修复的过程而已~
    18 条回复    2022-01-10 06:51:32 +08:00
    iyear
        1
    iyear  
       2021-12-30 09:55:41 +08:00 via Android
    写三次,就读了一次当然剩下两个阻塞出不来了
    wangdashuai
        2
    wangdashuai  
       2021-12-30 10:04:52 +08:00
    写个 woker 池处理,这样能保证 goroutine 数量不随任务增加。
    CEBBCAT
        3
    CEBBCAT  
       2021-12-30 10:07:44 +08:00 via Android
    queryAll 中的协程加上 select
    keepeye
        4
    keepeye  
       2021-12-30 10:10:31 +08:00
    不知道这个例子的想实现什么功能,仅为了修复而修复的话,可以给 chan 加上 buffer 或者写的时候用 select 就不会阻塞了
    vizee
        5
    vizee  
       2021-12-30 10:14:00 +08:00
    ch := make(chan int, 3)

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

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

    select {
    case ch <- query():
    default:
    }
    gamexg
        7
    gamexg  
       2021-12-30 10:43:12 +08:00
    @gamexg #6 select 也要保证 chan 至少有 1 的缓冲区
    xiaoFine
        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
    **/
    ```
    xiaoFine
        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
    **/
    ```
    hzzhzzdogee
        10
    hzzhzzdogee  
       2021-12-30 12:06:35 +08:00
    @xiaoFine #8 因为 100 毫米以后 query()才返回, 你直接打印 runtime.NumGoroutine()当然会不正确. 实际上 goroutine 并没有泄露
    zwpaper
        11
    zwpaper  
       2021-12-30 12:08:55 +08:00   ❤️ 1
    @xiaoFine #8 单从解决 Goroutine 泄漏来说,query 里有 sleep ,你得等 query 跑完了再打 Goroutine 数量,就能看到数量只有 1 ,但是确实让人想不明白写 3 次,读 1 次这个逻辑意义是什么
    xiaoFine
        12
    xiaoFine  
       2021-12-30 15:27:55 +08:00
    @zwpaper 我能找到的最早的出处是这样 https://medium.com/golangspec/goroutine-leak-400063aef468 ,应该就是单纯讨论 goroutine 泄漏的一个 demo ,不过确实学到了
    index90
        13
    index90  
       2021-12-30 19:24:58 +08:00   ❤️ 1
    所有 goroutine 都需要有个 ctx 或者类似的“控制线”,并且独立于“数据线”
    在业务逻辑结束之前,通过关闭“控制线”来结束所有 goroutine
    SorcererXW
        14
    SorcererXW  
       2021-12-30 21:27:19 +08:00
    写的时候 select 一下或者用 sync.once 包起来保证只写一次 channel
    更好的办法是传一个 context 进去,外部 defer 里面执行一下 cancel
    gjquoiai
        15
    gjquoiai  
       2021-12-31 18:58:44 +08:00
    你这个只能叫背压 demo ,并没有东西泄漏
    zinwalin
        16
    zinwalin  
       2022-01-07 16:01:26 +08:00
    为啥我能运行起来
    xoxo419
        18
    xoxo419  
    OP
       2022-01-10 06:51:32 +08:00   ❤️ 1
    @zinwalin 是可以运行的,泄露只是程序运行的越久占用内存就会越高 最后导致程序无响应
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5743 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 02:46 · PVG 10:46 · LAX 18:46 · JFK 21:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.