62

我正在学习围棋,到目前为止,我对它印象深刻。我已经阅读了 golang.org 上的所有在线文档,并且正在阅读 Chrisnall 的“The Go Programming Language Phrasebook”。我了解了渠道的概念,并认为它们将非常有用。但是,在此过程中,我一定错过了一些重要的事情,因为我看不到单向通道的意义。

如果我对它们的解释正确,只能接收只读通道而只能发送只写通道,那么为什么要有一个可以发送但永远不会接收的通道呢?它们可以从一个“方向”投射到另一个“方向”吗?如果是这样,那么,如果没有实际约束,那又有什么意义呢?它们仅仅是对客户代码通道目的的提示吗?

4

3 回答 3

100

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

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(),都会收到一个他们只能阅读的频道。这对于避免在编译时潜在地滥用通道非常有用。因为只读/只写通道是不同的类型,编译器可以使用其现有的类型检查机制来确保调用者不会尝试将内容写入它没有业务写入的通道。

于 2012-11-28T01:48:53.197 回答
9

我认为只读通道的主要动机是防止通道的损坏和恐慌。想象一下,如果您可以写入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)
}

您的连接处理可​​能会比接受新连接花费更长的时间,因此您希望有很多消费者和一个生产者。多个生产者比较困难(因为您需要协调谁关闭通道)但是您可以在通道发送中添加某种信号量样式的通道。

于 2012-11-28T18:45:48.883 回答
6

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

这很重要——如果通道端可以在不同时间任意重复用作发送方和接收方,则很难推断死锁问题。

于 2012-11-28T09:57:25.903 回答