3 回答
TA贡献1873条经验 获得超9个赞
可以将通道设置为对接收该通道的任何人都是只读的,而发送方仍具有一个双向通道,他们可以向其写入。例如:
func F() <-chan int {
// Create a regular, two-way channel.
c := make(chan int)
go func() {
defer close(c)
// Do stuff
c <- 123
}()
// Returning it, implicitely converts it to read-only,
// as per the function return value.
return c
}
呼叫的人F()都会收到一个只能阅读的频道。这对于避免在编译时潜在地滥用通道很有用。因为只读/只写通道是不同的类型,所以编译器可以使用其现有的类型检查机制来确保调用者不会尝试将内容写入没有业务写入的通道。
TA贡献1829条经验 获得超4个赞
我认为只读频道的主要动机是为了防止腐败和对该频道的恐慌。想象一下您是否可以写入由返回的通道time.After
。这可能会弄乱很多代码。
另外,如果您发生恐慌,您可能会:
关闭频道不止一次
写入关闭的频道
这些操作是只读通道的编译时错误,但是当多个go-routine可以写入/关闭通道时,它们可能会导致讨厌的竞争状况。
解决此问题的一种方法是永远不要关闭通道并让它们被垃圾回收。但是,close
这不仅用于清理,而且在通道范围超过以下范围时,它实际上也很有用:
func consumeAll(c <-chan bool) {
for b := range c {
...
}
}
如果该通道从未关闭,则该循环将永远不会结束。如果有多个go-routine正在写入一个通道,那么必须进行很多记账工作,然后再决定哪个程序将关闭该通道。
由于您无法关闭只读通道,因此可以更轻松地编写正确的代码。正如@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)
}
您的连接处理可能比接受新连接需要更长的时间,因此您希望拥有一个生产者的多个使用者。多个生产者比较困难(因为您需要协调关闭通道的人员),但是您可以在通道发送中添加某种信号量样式的通道。
TA贡献1833条经验 获得超4个赞
Go通道基于Hoare的通信顺序过程建模,Hoare的通信顺序过程是并发的过程代数,它以通信参与者(小“ a”)之间的事件流为中心。这样,通道之所以具有方向性,是因为它们具有发送端和接收端,即事件的产生者和事件的使用者。Occam和Limbo也使用了类似的模型。
这很重要-如果通道端可以在不同时间任意用作发送者和接收者,则很难为死锁问题辩解。
- 3 回答
- 0 关注
- 281 浏览
添加回答
举报