这段代码中应该有一个错误。我的伙伴告诉我,它包含一个内存泄漏,它发生在go函数完成之前在select语句中发生超时情况时,他还告诉我在ch中添加一个大小为1的缓冲区可以解决问题。但我很难理解为什么它会解决这个问题,如果有人可以解释一下,我会很感激吗?我试图自己寻找答案,但没有成功。
感谢。
func Read(url string, timeout time.Duration) (res *Response) {
ch := make(chan *Response)
go func() {
time.Sleep(time.Millisecond * 300)
ch <- Get(url)
}()
select {
case res = <-ch:
case <-time.After(timeout):
res = &Response{"Gateway timeout\n", 504}
}
}
答案 0 :(得分:2)
事实上,没有缓冲区的通道(称为同步)将阻止发送方和接收方,直到它们完成交换为止。
就像你必须把东西交给你的伴侣,你们两个都知道会面地点,但不是时间。到达那里的第一个将等待另一个,无论是发送者还是接收者。现在,鉴于计算机是愚蠢的:-)如果其中一个忘记约会,另一个将真正等待。
这里的具体错误是,当select选择time.After
时(即发生超时),将不再有人从<-ch
接收。永远不会。因此,穷人go func()
将永远坐在那里,等待某人带她*Response
,但没有人会出现。
这实际上并没有浪费任何CPU能力,但它浪费了内存:需要跟踪她的堆栈的通道,goroutine,无论多么小 - 以及她的本地变量。在整个过程终止或被杀死之前,永远不会回收内存。
在为很多客户服务的服务器中,这将很快建立起来,直到应用程序占用服务器的所有内存,并且 - 如果你很幸运 - 被操作系统安全措施杀死,而不会占用整个机器
使用缓冲通道是解决问题的一种方法,因为每当穷人go func()
准备好*Response
时,她就能将它存储到通道的缓冲区中,即使没有人有没有得到它,并平静地终止。一旦发生这种情况,Go的垃圾收集器会注意到没有实时goroutine正在保存指向该通道的任何指针,因此它将收集通道并指向它的*Response
并回收所有这些字节。