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工具检测这种泄漏?
答案 0 :(得分:11)
是的,你是对的(作为参考,这里是link to the slide)。在上面的代码中,只有一个已启动的goroutine将终止,其余的将在尝试发送频道c
时挂起。
详图:
c
是一个无缓冲的频道return
声明replicas
c
请注意,取决于replicas
(len(replicas)
)的元素数量:
0
:First()
将永久阻止(没有人在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...
}
检测泄漏的一些工具/资源: