Go的单向频道有什么意义?

时间:2012-11-28 01:15:16

标签: go

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

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

3 个答案:

答案 0 :(得分:82)

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

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(),接收他们只能阅读的频道。 这对于在编译时捕获通道的潜在错误使用非常有用。 因为只读/只写通道是不同的类型,所以编译器可以使用 它现有的类型检查机制,以确保调用者不会尝试编写 这是一个没有商业写作的频道。

答案 1 :(得分:7)

我认为只读渠道的主要动机是防止渠道的腐败和恐慌。想象一下,如果你可以写信给time.After返回的频道。这可能会搞砸很多代码。

此外,如果您:

,可能会发生恐慌
  • 不止一次关闭频道
  • 写入已关闭的频道

这些操作是只读通道的编译时错误,但是当多个go-routines可以写入/关闭通道时,它们会导致恶劣的竞争条件。

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

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

如果频道从未关闭,此循环将永远不会结束。如果多个常规程序正在写入一个频道,那么就需要继续进行大量的记录,以决定哪一个将关闭该频道。

由于无法关闭只读通道,因此可以更轻松地编写正确的代码。正如@jimt在他的评论中指出的那样,你不能将只读通道转换为可写通道,所以你可以保证只有部分可以访问可写通道版本的代码可以关闭/写入它。 / p>

修改

至于拥有多个读者,只要您考虑到这一点,这就完全没问题了。当在生产者/消费者模型中使用时,这尤其有用。例如,假设您有一个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)
}

可能您的连接处理比接受新连接需要更长的时间,因此您希望拥有一个生产者的许多消费者。多个生产者更难(因为你需要协调谁关闭了频道),但你可以在频道发送中添加某种信号量风格的频道。

答案 2 :(得分:3)

Go渠道是以Hoare的Communicating Sequential Processes建模的,这是一种并发的流程代数,面向沟通参与者之间的事件流(小'a')。这样,频道具有方向,因为它们具有发送端和接收端,即事件的产生者和事件的消费者。 Occam和Limbo也使用了类似的模型。

这很重要 - 如果频道端可以在不同时间任意重新用作发送方和接收方,则很难解决死锁问题。