错误检测,选择转到频道

时间:2015-03-28 22:52:23

标签: go

这段代码中应该有一个错误。我的伙伴告诉我,它包含一个内存泄漏,它发生在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}
    }
}

1 个答案:

答案 0 :(得分:2)

事实上,没有缓冲区的通道(称为同步)将阻止发送方和接收方,直到它们完成交换为止。

就像你必须把东西交给你的伴侣,你们两个都知道会面地点,但不是时间。到达那里的第一个将等待另一个,无论是发送者还是接收者。现在,鉴于计算机是愚蠢的:-)如果其中一个忘记约会,另一个将真正等待。

这里的具体错误是,当select选择time.After时(即发生超时),将不再有人从<-ch接收。永远不会。因此,穷人go func()将永远坐在那里,等待某人带她*Response,但没有人会出现。

这实际上并没有浪费任何CPU能力,但它浪费了内存:需要跟踪她的堆栈的通道,goroutine,无论多么小 - 以及她的本地变量。在整个过程终止或被杀死之前,永远不会回收内存。

在为很多客户服务的服务器中,这将很快建立起来,直到应用程序占用服务器的所有内存,并且 - 如果你很幸运 - 被操作系统安全措施杀死,而不会占用整个机器

使用缓冲通道是解决问题的一种方法,因为每当穷人go func()准备好*Response时,她就能将它存储到通道的缓冲区中,即使没有人有没有得到它,并平静地终止。一旦发生这种情况,Go的垃圾收集器会注意到没有实时goroutine正在保存指向该通道的任何指针,因此它将收集通道并指向它的*Response并回收所有这些字节。