The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xoxo419

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

  •  
  •   xoxo419 · Dec 30, 2021 · 3849 views
    This topic created in 1593 days ago, the information mentioned may be changed or developed.
    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
    }
    
    Supplement 1  ·  Dec 30, 2021
    只是单纯讨论 goroutine 泄漏的一个 demo ,想从这个 demo 去学习如何分析泄露和修复的过程而已~
    18 replies    2022-01-10 06:51:32 +08:00
    iyear
        1
    iyear  
       Dec 30, 2021 via Android
    写三次,就读了一次当然剩下两个阻塞出不来了
    wangdashuai
        2
    wangdashuai  
       Dec 30, 2021
    写个 woker 池处理,这样能保证 goroutine 数量不随任务增加。
    CEBBCAT
        3
    CEBBCAT  
       Dec 30, 2021 via Android
    queryAll 中的协程加上 select
    keepeye
        4
    keepeye  
       Dec 30, 2021
    不知道这个例子的想实现什么功能,仅为了修复而修复的话,可以给 chan 加上 buffer 或者写的时候用 select 就不会阻塞了
    vizee
        5
    vizee  
       Dec 30, 2021
    ch := make(chan int, 3)

    脑筋急转弯是吧
    gamexg
        6
    gamexg  
       Dec 30, 2021   ❤️ 1
    如楼上,建立 3 缓冲区的 chan

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

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