【问题标题】:Is this func possible to cause goroutine leak这个函数是否可能导致 goroutine 泄漏
【发布时间】:2018-02-23 04:40:16
【问题描述】:
func startTimer(ctx context.Context, intervalTime int) {
    intervalChan := make(chan bool) 
    go func() {
        for {
            select {
            case <-ctx.Done():
                return
            case <-time.After(time.Second * time.Duration(intervalTime)):
                intervalChan <- true
            }
        }
    }()


    for {
        select {
        case <-ctx.Done():
            return
        case <-intervalChan:
            doSomething()
    }
}

你好,我上面写了一个func,想知道会不会导致goroutine泄漏。

例如,第一个 select 语句向 intervalChan 发送一个 true,然后第二个 select 语句从 ctx.Done() 接收 Done 标志并返回。 goroutine 会永远被阻塞吗?

【问题讨论】:

  • 你用 defer 取消你的上下文吗?文档中的第 3 段golang.org/pkg/context.. 还有为什么使用intervalChan 它更干净地实现一个带超时的上下文,因为你已经在使用一个上下文并删除intervalChan golang.org/pkg/context/#example_WithTimeout
  • 嗨@reticentroot,在我的原始程序中,上下文用于手动取消计时器。并且使用上下文的超时可能在功能上与我的 func 不同

标签: go memory-leaks goroutine


【解决方案1】:

我不能每次都复制这种行为,但可能会有一些泄漏。如果doSomething 做一些繁重的计算,同时goroutine 在intervalChan &lt;- true 上被阻塞,因为它无法推入通道。在doSomething 完成执行并取消上下文后,startTimer 存在于goroutine 之前,这将导致goroutine 阻塞,因为intervalChan 没有任何消费者。

go version go1.8.3 darwin/amd64

package main

import (
    "context"
    "fmt"
    "time"
)

func startTimer(ctx context.Context, intervalTime int) {
    intervalChan := make(chan bool)
    go func() {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("Done from inside of goroutine.")
                return
            case <-time.After(time.Second * time.Duration(intervalTime)):
                fmt.Println("Interval reached.")
                intervalChan <- true
            }
        }
    }()

    for {
        select {
        case <-ctx.Done():
            fmt.Println("Done from startTimer.")
            return
        case <-intervalChan:
            time.Sleep(10 * time.Second)
            fmt.Println("Done")
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    startTimer(ctx, 2)
}

【讨论】:

  • 感谢您的回复。在我的程序中 doSomething() 非常轻,因此几乎不会导致泄漏。显然这是一个潜在的问题,你有什么解决办法吗?
【解决方案2】:

你的第一个 goroutine 唯一可能被无限期阻塞的地方是intervalChan &lt;- true。将它放在另一个选择块中以便能够取消该发送:

go func() {
    for {
        select {
        case <-ctx.Done():
            return
        case <-time.After(time.Second * time.Duration(intervalTime)):
            select {
            case <-ctx.Done():
                return
            case intervalChan <- true:
            }
        }
    }
}()

【讨论】:

    猜你喜欢
    • 2013-08-04
    • 2015-07-30
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2019-08-28
    • 2014-12-09
    • 1970-01-01
    • 2012-05-12
    相关资源
    最近更新 更多