icz*_*cza 41

我认为这个规格非常明确.规格:渠道类型:

当您有多个同时执行的goroutine时,通道提供了允许goroutine相互通信的最简单方法.

一种通信方式是通过两个goroutine可见的"共享"变量,但这需要适当的锁定/同步访问.

不要通过共享内存进行通信; 相反,通过沟通分享记忆.

因此,不是将消息放入共享切片,例如,您可以创建一个通道(两个goroutine都可见),并且没有任何外部同步/锁定,一个goroutine可以通过通道发送消息(值),另一个goroutine可以接收他们.

在任何给定时间,只有一个goroutine可以访问该值.根据设计,数据竞争不会发生.

所以事实上,任何数量的goroutine都可以在同一个通道上发送值,任何数量的goroutine都可以从中接收值,仍然没有任何进一步的同步.有关详细信息,请参阅相关问题:如果我正确使用频道,是否需要使用互斥锁?

频道示例

让我们看一个例子,我们为并发计算目的开始另外两个goroutine.我们将一个数字传递给第一个数字,它会增加1,并在第二个通道上传递结果.第二个goroutine将收到一个数字,乘以10并将其传递到结果通道:

func AddOne(ch chan<- int, i int) {
    i++
    ch <- i
}

func MulBy10(ch <-chan int, resch chan<- int) {
    i := <-ch
    i *= 10
    resch <- i
}

这是如何调用/使用它:

func main() {
    ch := make(chan int)
    resch := make(chan int)

    go AddOne(ch, 9)
    go MulBy10(ch, resch)

    result := <-resch
    fmt.Println("Result:", result)
}
MulBy10()AddOne()main()MulBy10()
Result: 100

语言支持

有许多语言结构可以方便地使用频道,例如:

for ... rangeselectv, ok := <-chlen()cap()

其他用途

有关更实际的示例,请参阅如何使用通道实现工作池.类似的用途是将值从生产者分配给消费者.

另一个实际示例是使用缓冲通道实现内存池.

另一个实际的例子是经纪人的优雅实施.

time.After()
ch := make(chan int)

select {
case i := <-ch:
    fmt.Println("Received:", i)
case <-time.After(time.Second):
    fmt.Println("Timeout, no value received")
}

它可用于等待某个值的最大时间量,但如果其他goroutine无法提供该值,我们可能会决定做其他事情.

chan int0chan struct{}for ... rangesync.WaitGroup

进一步阅读

值得了解通道公理以避免出现令人惊讶的行为:非初始化通道如何表现?

  • 我喜欢这个答案,尤其是有关使用time.After(time.Second)的内容。我不知道可以做到这一点。 (3认同)