Go 什么';围棋中单向通道的意义是什么?

Go 什么';围棋中单向通道的意义是什么?,go,Go,我正在学习围棋,到目前为止我对它印象深刻。我已经阅读了golang.org上的所有在线文档,并且已经完成了Chrisnall的“Go编程语言手册”的一半。我了解了频道的概念,并认为它们将非常有用。然而,我肯定错过了一些重要的事情,因为我看不到单向通道的意义 如果我对它们的解释正确,只读通道只能在上接收,而写通道只能在上传输,那么为什么有一个可以发送到但永远不能接收的通道呢?它们能从一个“方向”投射到另一个“方向”吗?如果是这样的话,如果没有实际的约束又有什么意义呢?它们只不过是对客户机代码的一个

我正在学习围棋,到目前为止我对它印象深刻。我已经阅读了golang.org上的所有在线文档,并且已经完成了Chrisnall的“Go编程语言手册”的一半。我了解了频道的概念,并认为它们将非常有用。然而,我肯定错过了一些重要的事情,因为我看不到单向通道的意义


如果我对它们的解释正确,只读通道只能在上接收,而写通道只能在上传输,那么为什么有一个可以发送到但永远不能接收的通道呢?它们能从一个“方向”投射到另一个“方向”吗?如果是这样的话,如果没有实际的约束又有什么意义呢?它们只不过是对客户机代码的一个提示,说明了频道的用途吗?

频道可以对任何接收到它的人设置为只读,而发送者仍然有一个双向频道,他们可以写入。例如:

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
}

func F()Go通道是以霍尔的通信顺序进程为模型的,这是一种面向通信参与者之间的事件流(小“a”)的并发进程代数。因此,通道具有方向,因为它们具有发送端和接收端,即事件的生产者和消费者。Occam和Limbo中也使用了类似的模型


这一点很重要-如果通道端可以在不同的时间任意重复用作发送方和接收方,则很难解释死锁问题。

我认为只读通道的主要动机是防止通道损坏和恐慌。想象一下,如果您可以写入由返回的通道。这可能会弄乱很多代码

此外,如果您:

  • 多次关闭频道
  • 写入一个封闭的通道
这些操作是只读通道的编译时错误,但当多个go例程可以写入/关闭通道时,它们可能会导致恶劣的竞争条件

解决这个问题的一个方法是永远不要关闭通道,让它们被垃圾收集。但是,
close
不仅用于清理,而且在通道范围内时,它实际上也有用途:

func consumeAll(c <-chan bool) {
    for b := range c {
        ...
    }
}

func consumerall(c)但在接收端,调用方不能将其强制转换回双向?这就是我这里要说的。它只能通过返回来隐式转换?您不能将其转换回可写版本。编译器会抱怨
无法转换c(输入感谢添加。我觉得这很有价值。也许这应该是一个新问题,但多个发件人共享一个“端”是否安全多个接受者从另一个接受者那里阅读?我不知道Go频道是否支持多个作者或读者。这里的一个答案断言它支持,而语言引用暗示它不支持(但有点模棱两可)。这并不重要;相反,JCSP Java通道库区分了一对一通道和任意对一通道(etc)因为它必须这样做,而Go编译器原则上可以自动计算出来。我在这里找到了一个很好的答案,非常感谢!这里有很多很好的信息。我不知道它比@jimt更好地回答了原始问题,所以我将把它作为可接受的答案,但这很好地澄清了接下来的问题t我在评论中提到的问题。我肯定会投赞成票。我没想到会得到公认的答案=)。我只是觉得这些信息会有用。
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)
}