什么是Golang频道?

时间:2016-10-03 07:45:47

标签: go channel

查看一些Golang代码时,我发现了以下内容:

  ch := make(chan int)

我在一个在线教程中查找了Golang频道如何工作:

https://tour.golang.org/concurrency/2

但我发现这个例子不清楚。

有人可以给我一个简单的解释和使用频道的例子吗?

4 个答案:

答案 0 :(得分:41)

是Golang中的一个频道。简单来说,你可以认为它是一个盒子,你在其中放置一个项目然后从另一端拾取它。(顺序无关紧要)像

这样的东西

无缓冲频道

enter image description here

缓冲频道

enter image description here

这是我为您理解频道而编写的小代码。现在改变go例程的顺序并查看输出。每次输出可能不同。

php /path/to/artisan schedule:run >> /dev/null 2>&1

为了更好地理解,请访问此博客,其中在GUI中描述了例程和通道。

访问http://divan.github.io/posts/go_concurrency_visualize/

答案 1 :(得分:35)

我认为这个规格非常明确。 Spec: Channel types:

  

渠道为concurrently executing functions提供了一种机制,可以通过指定元素类型的sendingreceiving值进行通信。

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

通信的一种方式是通过两个goroutine可见的“共享”变量,但这需要正确的锁定/同步访问。

相反,Go赞成频道。引自Effective Go: Share by communicating

  

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

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

  

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

事实上,任何数量的goroutine都可以在同一个通道上发送值,任何数量的goroutine都可以从中接收值,但仍然没有任何进一步的同步。有关详细信息,请参阅相关问题:If I am using channels properly should I need to use mutexes?

频道示例

让我们看一个示例,其中我们为并发计算目的启动了2个额外的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)
}

通过渠道进行沟通也会照顾到彼此等待的goroutine。在此示例中,表示MulBy10()将等待AddOne()递送递增的数字,main()将等待MulBy10(),然后才能打印结果。按预期输出(在Go Playground上尝试):

Result: 100

语言支持

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

  • 频道上的for ... range会循环播放从频道收到的值,直到频道关闭。
  • select语句可用于列出多个频道操作,例如在频道上发送从频道接收,以及可以继续进行的操作将选择无阻塞(如果有多个可以继续,则随机选择;如果没有准备就会阻止)。
  • 接收运算符有一种特殊形式,可让您检查频道是否已关闭(除了接收值):v, ok := <-ch
  • 内置len()函数告诉排队(未读)的元素数量; builting cap()函数返回通道缓冲区容量。

其他用途

有关更实际的示例,请参阅如何使用渠道来实施worker pool。类似的用途是从producer to consumer(s)分发值。

另一个实际例子是实现memory pool using buffered channels

另一个实际例子是an elegant implementation of a broker

通道通常用于超时某些阻塞操作,利用time.After()返回的通道,该通道在指定的延迟/持续时间后“触发”(“触发”表示将在其上发送一个值)。请参阅此示例以进行演示(在Go Playground上尝试):

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 int,并在其上发送任何价值,例如0。但由于发送的值不包含任何信息,因此您可以将其声明为chan struct{}。或者甚至更好,如果您只需要一次性信令,您可以关闭可以使用for ... range在另一侧拦截的频道,或从其接收(因为从封闭频道接收立即进行,产生元素类型的zero value)。还要知道即使某个信道可能用于此类信令,也可以使用更好的替代方法:sync.WaitGroup

进一步阅读

值得了解通道公理以避免出现令人惊讶的行为:How does a non initialized channel behave?

The Go Blog: Share Memory By Communicating

The Go Blog: Go Concurrency Patterns: Pipelines and cancellation

The Go Blog: Advanced Go Concurrency Patterns

答案 2 :(得分:1)

要更好地理解Go中通道的概念,您可以先了解Go例程,然后阅读下面的链接,该链接清楚地解释了golang中通道的概念。

https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb

请参考此链接,以借助示例来详细了解频道。

答案 3 :(得分:1)

这个概念与从一开始就在Unix / Linux中使用过的东西非常相似:管道。

这些是语言内置的可靠的进程间/线程间通信工具。非常方便。