为了账号安全,请及时绑定邮箱和手机立即绑定

错误检测,选择频道

错误检测,选择频道

Go
隔江千里 2021-09-27 14:23:01
这段代码应该有一个错误。我的伙伴告诉我它包含内存泄漏,并且在 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 回答

?
繁星淼淼

TA贡献1775条经验 获得超11个赞

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

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

The specific bug here is that when the select chooses the time.After(that is, the timeout occurs) nobody will be there to receive from <-chanymore. 永远不会。所以穷人go func()会永远坐在那里,等待有人带走她*Response,但永远不会有人出现。

这实际上并没有浪费任何 CPU 能力,而是浪费了内存:需要跟踪通道、goroutine、她的堆栈(无论多小)和她的局部变量。在整个进程终止或被杀死之前,永远不会回收内存。

在为大量客户端提供服务的服务器中,这种情况会迅速增加,直到应用程序吃掉服务器的所有内存,并且——如果你幸运的话——被操作系统安全措施杀死,而不会关闭整个机器。

使用缓冲通道,是解决问题的一种方式,因为那样的话,只要穷人go func()有她*Response准备好了,她就可以将其存储到通道的缓冲区,即使没有人在那里得到它,而和平结束。一旦发生这种情况,Go 的垃圾收集器会注意到没有活动的 goroutine 再持有任何指向该通道的指针,因此它将收集该通道及其*Response指向并回收所有这些字节。


查看完整回答
反对 回复 2021-09-27
  • 1 回答
  • 0 关注
  • 164 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信