【问题标题】:Is there a resource leak here?这里有资源泄漏吗?
【发布时间】:2018-09-13 20:39:06
【问题描述】:
func First(query string, replicas ...Search) Result {
  c := make(chan Result)
  searchReplica := func(i int) {
    c <- replicas[i](query)
  }
  for i := range replicas {
    go searchReplica(i)
  }
  return <-c
}

这个函数来自 Rob Pike 在 2012 年关于 go concurrency patterns 的幻灯片。我认为这个函数存在资源泄漏。由于函数在通道 c 上发生第一个发送和接收对后返回,其他 go 例程尝试在通道 c 上发送。所以这里存在资源泄漏。任何人都知道golang可以证实这一点?以及如何使用哪种 golang 工具检测这种泄漏?

【问题讨论】:

    标签: go concurrency channel goroutine


    【解决方案1】:

    是的,你是对的(供参考,这里是link to the slide)。在上面的代码中,只有一个启动的 goroutine 将终止,其余的将挂起尝试在频道 c 上发送。

    细化:

    • c 是一个无缓冲通道
    • return 语句中只有一个接收操作
    • replicas的每个元素启动一个新的goroutine
    • 每个启动的 goroutine 在通道 c 上发送一个值
    • 由于只有 1 个接收,一个 goroutine 将能够在其上发送一个值,其余的将永远阻塞

    注意取决于replicas的元素个数(也就是len(replicas)):

    • 如果是0First() 将永远阻塞(没有人在c 上发送任何内容)
    • 如果是 1: 会按预期工作
    • 如果是&gt; 1:那么它会泄漏资源

    以下修改版本不会泄漏 goroutine,通过使用非阻塞发送(在 selectdefault 分支的帮助下):

    searchReplica := func(i int) {
        select {
        case c <- replicas[i](query):
        default:
        }
    }
    

    第一个准备好结果的 goroutine 将在通道 c 上发送它,这将由运行 First() 的 goroutine 在return 语句中接收。所有其他 goroutine 在获得结果时将尝试在通道上发送,并且“看到”它尚未准备好(发送将阻塞,因为没有人准备好接收它),将选择 default 分支,因此goroutine 会正常结束。

    另一种解决方法是使用缓冲通道:

    c := make(chan Result, len(replicas))
    

    这样发送操作就不会阻塞。当然,只有一个(第一个发送的)值会从通道接收并返回。

    请注意,如果len(replicas)0,则具有上述任何修复的解决方案仍会阻塞。为避免这种情况,First() 应明确检查这一点,例如:

    func First(query string, replicas ...Search) Result {
        if len(replicas) == 0 {
            return Result{}
        }
        // ...rest of the code...
    }
    

    一些检测泄漏的工具/资源:

    【讨论】:

    • 感谢您的推理。这很清楚。请您再次提供一些提示或建议,说明什么是解决此类问题的正确 golang 工具,例如资源泄漏
    • @David 这部分问题与 Stack Overflow 无关,但请参阅编辑后的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多