这里有资源泄漏吗?

Dav*_*vid 5 concurrency channel go 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
}
Run Code Online (Sandbox Code Playgroud)

这个函数来自Rob Pike在2012年的并发模式上的幻灯片.我认为这个函数存在资源泄漏.当第一个发送和接收对发生在通道c上后,函数返回,其他的例程尝试在通道c上发送.所以这里有资源泄漏.谁知道golang好可以证实这一点?如何使用什么样的golang工具检测这种泄漏?

icz*_*cza 11

是的,你是对的(作为参考,这里是幻灯片的链接).在上面的代码中,只有一个启动的goroutine将终止,其余的将在尝试发送通道时挂起c.

详图:

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

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

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

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

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

第一个够程愿与结果将发送信道c将由够程运行的接收First(),在return声明.所有其他goroutines,当他们有结果将尝试发送通道,并"看到"它没有准备好(发送将阻止,因为没有人准备从它接收),default分支将被选择,因此goroutine将结束一般.

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

c := make(chan Result, len(replicas))
Run Code Online (Sandbox Code Playgroud)

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

请注意,以上述任何修复的解决方案仍然会阻止如果len(replicas)是0.为避免这种情况,First()应明确检查,例如:

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

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