当我做一些去练习代码时,我遇到一个问题,一个频道可以这样关闭两次:
// jobs.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
jobs := make(chan int, 5)
done := make(chan bool)
go func() {
for {
j,more := <-jobs
fmt.Println("receive close: ", j, more)
done <- true
}
}()
close(jobs)
<- done
}
输出:
~ go run jobs.go
Hello, playground
receive close: 0 false
receive close: 0 false
但是当我手动关闭频道两次时,我得到panic: close of closed channel
。
为什么上面的代码可以接收两次?
答案 0 :(得分:3)
一个频道只能关闭一次,试图关闭一个封闭的频道恐慌。
但来自封闭频道的receiving不受限制,从封闭频道接收:
closed频道上的接收操作始终可以立即进行,在收到任何先前发送的值后,会产生元素类型zero value。
Go应用程序运行直到其主要goroutine运行(给定“正常”情况),或者从另一个角度来看:Go应用程序在其主要goroutine终止时终止,即main()
函数返回。它不会等待其他非main
goroutines完成。
你开始了一个无尽的for
循环的第二个goroutine,无法终止。因此循环将一直持续到main()
函数 - 它在并发的主要goroutine-返回中运行。由于for
循环首先从jobs
接收,它等待主goroutine关闭它(此接收操作只能继续)。然后主goroutine想要从done
接收,以便等到第二个goroutine发送一个值。然后主要的goroutine“随意”随时终止。运行循环的第二个goroutine可能会从jobs
收到一个额外的值,因为它已关闭,但done
上的后续发送将被阻止,因为没有人再接收它(并且它没有缓冲)。
从频道接收直到关闭通常是使用for range
完成的,如果频道已关闭则退出:
for j := range jobs {
fmt.Println("received: ", j)
done <- true
}
当然这会导致你的情况出现死锁,因为在jobs
上没有人发送任何内容时永远不会到达循环体,因此循环永远不会进入其身体以在{{1}上发送值这是主要的goroutine等待的东西。
答案 1 :(得分:1)
jobs
频道未关闭两次。它只在调用close(jobs)
时关闭一次。您看到的输出是由于goroutine和主线程的执行方式。
当goroutine开火时,它不会立即开始运行。相反,程序控制转到这一部分:
close(jobs) // <--
<- done
}
和jobs
已关闭。主线程然后在done
上的下一次接收时挂起。
现在进行上下文切换,goroutine开始运行。它从已关闭的jobs
读取,打印相应的值(false
为more
,表示已关闭的频道),并沿true
发送done
。
但是,循环能够再次执行,而下一个goroutine会在done
上发送。现在main
再次唤醒,在done
收到并终止该程序。
答案 2 :(得分:1)
去频道没关闭twise。你传递完毕&lt; - 首次打印后的真实
j,more := <-jobs
fmt.Println("receive close: ", j, more)
done <- true
所以它打印了两次
receive close: 0 false
receive close: 0 false
如果您在打印前使用完成&lt; - true,那么它将仅打印一次,将被关闭。
done <- true
j,more := <-jobs
fmt.Println("receive close: ", j, more)
输出:
receive close: 0 false