这里有资源泄漏吗?

时间:2018-04-04 10:10:37

标签: go concurrency channel goroutine

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年的并发模式上的幻灯片。我认为这个功能有资源泄漏。由于函数在第一次发送后返回&amp;接收对发生在通道c上,另一个程序尝试发送通道c。所以这里有资源泄漏。谁知道golang好可以证实这一点?如何使用什么样的golang工具检测这种泄漏?

1 个答案:

答案 0 :(得分:11)

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

详图:

  • c是一个无缓冲的频道
  • return声明
  • 中只有一个接收操作
  • replicas
  • 的每个元素启动新的goroutine
  • 每个已启动的goroutine都会在频道c
  • 上发送一个值
  • 因为只有一个接收它,一个goroutine将能够发送一个值,其余将永远阻止

请注意,取决于replicaslen(replicas))的元素数量:

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

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

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

第一个准备结果的goroutine将在c频道上发送,该频道将在First()语句中由运行return的goroutine接收。所有其他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...
}

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