简体   繁体   English

这里有资源泄漏吗?

[英]Is there a resource leak here?

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
}

This function is from the slides of Rob Pike on go concurrency patterns in 2012. I think there is a resource leak in this function. 这个函数来自Rob Pike在2012年的并发模式上的幻灯片。我认为这个函数存在资源泄漏。 As the function return after the first send & receive pair happens on channel c, the other go routines try to send on channel c. 当第一个发送和接收对发生在通道c上后,函数返回,其他的例程尝试在通道c上发送。 So there is a resource leak here. 所以这里有资源泄漏。 Anyone knows golang well can confirm this? 谁知道golang好可以证实这一点? And how can I detect this leak using what kind of golang tooling? 如何使用什么样的golang工具检测这种泄漏?

Yes, you are right (for reference, here's the link to the slide ). 是的,你是对的(作为参考,这里是幻灯片链接 )。 In the above code only one launched goroutine will terminate, the rest will hang on attempting to send on channel c . 在上面的代码中,只有一个启动的goroutine将终止,其余的将在尝试发送通道c挂起。

Detailing: 详图:

  • c is an unbuffered channel c是无缓冲的通道
  • there is only a single receive operation, in the return statement return语句中只有一个接收操作
  • A new goroutine is launched for each element of replicas replicas每个元素启动了一个新的goroutine
  • each launched goroutine sends a value on channel c 每个启动的goroutine在通道c上发送一个值
  • since there is only 1 receive from it, one goroutine will be able to send a value on it, the rest will block forever 因为只有一个接收它,一个goroutine将能够发送一个值,其余将永远阻止

Note that depending on the number of elements of replicas (which is len(replicas) ): 请注意,根据replicas的元素数量( len(replicas) ):

  • if it's 0 : First() would block forever (no one sends anything on c ) 如果它是0First()会永远阻塞(没有人在c上发送任何东西)
  • if it's 1 : would work as expected 如果它是1 :将按预期工作
  • if it's > 1 : then it leaks resources 如果它> 1 :那么它会泄漏资源

The following modified version will not leak goroutines, by using a non-blocking send (with the help of select with default branch): 以下修改版本不会通过使用非阻塞发送泄漏goroutine(在使用default分支的select的帮助default ):

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

The first goroutine ready with the result will send it on channel c which will be received by the goroutine running First() , in the return statement. 准备好结果的第一个goroutine将在通道c上发送它,它将由return语句中运行First()的goroutine接收。 All other goroutines when they have the result will attempt to send on the channel, and "seeing" that it's not ready (send would block because nobody is ready to receive from it), the default branch will be chosen, and thus the goroutine will end normally. 所有其他goroutines,当他们有结果将尝试发送通道,并“看到”它没有准备好(发送将阻止因为没有人准备从它接收),将选择default分支,因此goroutine将通常结束。

Another way to fix it would be to use a buffered channel: 另一种解决方法是使用缓冲通道:

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

And this way the send operations would not block. 这样发送操作就不会阻塞。 And of course only one (the first sent) value will be received from the channel and returned. 当然,只有一个(第一个发送的)值将从频道收到并返回。

Note that the solution with any of the above fixes would still block if len(replicas) is 0 . 请注意,如果len(replicas)0 ,则具有上述任何修复的解决方案仍会阻止。 To avoid that, First() should check this explicitly, eg: 为避免这种情况, First()应明确检查,例如:

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

Some tools / resources to detect leaks: 一些用于检测泄漏的工具/资源:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM