我认为只读通道的主要动机是防止通道的损坏和恐慌。想象一下,如果您可以写入time.After
. 这可能会弄乱很多代码。
此外,如果您:
这些操作是只读通道的编译时错误,但是当多个 go-routine 可以写入/关闭通道时,它们可能会导致令人讨厌的竞争条件。
解决这个问题的一种方法是永远不要关闭通道并让它们被垃圾收集。但是,close
不仅用于清理,而且在通道范围内时它实际上是有用的:
func consumeAll(c <-chan bool) {
for b := range c {
...
}
}
如果通道从未关闭,则此循环将永远不会结束。如果多个 go-routines 正在写入一个通道,那么有很多簿记必须继续决定哪个将关闭通道。
由于您无法关闭只读通道,因此可以更轻松地编写正确的代码。正如@jimt 在他的评论中指出的那样,您无法将只读通道转换为可写通道,因此您可以保证只有可以访问通道的可写版本的部分代码可以关闭/写入它。
编辑:
至于有多个读者,这完全没问题,只要你考虑到它。这在生产者/消费者模型中使用时特别有用。例如,假设您有一个 TCP 服务器,它只接受连接并将它们写入工作线程队列:
func produce(l *net.TCPListener, c chan<- net.Conn) {
for {
conn, _ := l.Accept()
c<-conn
}
}
func consume(c <-chan net.Conn) {
for conn := range c {
// do something with conn
}
}
func main() {
c := make(chan net.Conn, 10)
for i := 0; i < 10; i++ {
go consume(c)
}
addr := net.TCPAddr{net.ParseIP("127.0.0.1"), 3000}
l, _ := net.ListenTCP("tcp", &addr)
produce(l, c)
}
您的连接处理可能会比接受新连接花费更长的时间,因此您希望有很多消费者和一个生产者。多个生产者比较困难(因为您需要协调谁关闭通道)但是您可以在通道发送中添加某种信号量样式的通道。