将以下代码视为简化示例:
func printer(c <-chan int) {
for {
fmt.Print(<-c)
}
}
func provide() {
c := make(chan int)
go printer(c)
for i := 1; i <= 100; i++ {
c <- i
}
}
函数provide
创建一个go例程printer
,用于打印provide
生成的数据。
我的问题是,provide
返回后会发生什么,printer
会在空信道上开始阻塞。 go例程是否会泄漏,因为没有对c
的进一步引用,或者垃圾收集器是否会抓住这个案例并处理go例程和c
?
如果确实存在此类代码导致内存泄漏的情况,我可以采取哪些策略来防止此类内存泄漏?
答案 0 :(得分:10)
关闭频道。从封闭通道读取总是成功,返回相应的零值。可选的第二个布尔值返回值表示第一个值的有效性。
在表单的赋值或初始化中使用的接收表达式
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
产生类型
bool
的其他结果,报告通信是否成功。如果接收到的值是通过成功的发送操作传递给通道,则ok
的值为true;如果由于通道关闭且为空而产生零值,则返回false。
func printer(c <-chan int) {
for {
v, ok := <-c
if !ok { // chan closed
return
}
// v is valid
fmt.Println(v)
}
}
func provide() {
c := make(chan int)
go printer(c)
for i := 1; i <= 100; i++ {
c <- i
}
close(c)
}
答案 1 :(得分:0)
尝试以下程序来验证这确实泄漏了内存。请注意,这个程序很快就会占用你的RAM;准备杀了它。
package main
func worker(c <-chan int) {
var i int
for {
i += <-c
}
}
func wrapper() {
c := make(chan int)
go worker(c)
for i := 0; i < 0xff; i++ {
c <- i
}
}
func main() {
for {
wrapper()
}
}
为了解决泄漏问题,请关闭现在孤立的go例程引用的通道。运行时注意到仅从封闭通道读取的Go例程将永远不会继续并继续释放它。固定代码如下所示:
package main
func worker(c <-chan int) {
var i int
for {
i += <-c
}
}
func wrapper() {
c := make(chan int)
defer close(c) // fix here
go worker(c)
for i := 0; i < 0xff; i++ {
c <- i
}
}
func main() {
for {
wrapper()
}
}