通道有什么用?
What are channels used for?
在查看一些 Go 代码时,我发现了以下内容:
ch := make(chan int)
我在在线教程中查找了 Go Channels 的工作原理:
https://tour.golang.org/concurrency/2
但是我觉得这个例子不清楚。
谁能给我简单的解释和渠道使用的例子?
我认为规范对此非常清楚。 Spec: Channel types:
A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.
当您有多个并发执行的 goroutine 时,通道提供了允许 goroutine 相互通信的最简单方法。
一种通信方式是通过两个 goroutine 都可见的“共享”变量,但这需要适当的锁定/同步访问。
相反,Go 偏爱通道。引用自 Effective Go: Share by communicating:
Do not communicate by sharing memory; instead, share memory by communicating.
因此,您可以创建一个通道(对两个 goroutine 都可见),而不是将消息放入共享切片,而无需任何外部同步/锁定,一个 goroutine 可以通过通道发送消息(值),并且另一个 goroutine 可以接收它们。
Only one goroutine has access to the value at any given time. Data races cannot occur, by design.
所以事实上,任意数量的 goroutines 都可以在同一个通道上发送值,并且任意数量的 goroutines 都可以从中接收值,仍然不需要任何进一步的同步。有关详细信息,请参阅相关问题:
频道示例
让我们看一个例子,我们启动了 2 个额外的 goroutines 用于并发计算。我们将一个数字传递给第一个,将其加 1,并将结果传递到第二个通道。第 2 个 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)
}
通过通道进行通信还可以处理相互等待的 goroutines。在这个例子中,它意味着 MulBy10()
将等待 AddOne()
交付递增的数字,而 main()
将等待 MulBy10()
在打印结果之前。按预期输出(在 Go Playground 上尝试):
Result: 100
语言支持
有许多语言结构可以方便地使用频道,例如:
- 通道上的
for ... range
循环处理从通道接收的值,直到通道关闭。
select
语句可用于列出多个通道操作,例如在通道上发送和从通道接收, 可以不阻塞继续进行的将被选中(如果有多个可以继续进行则随机选择;如果 none 准备就绪将阻塞)。
- 接收运算符有一种特殊形式,它允许您检查通道是否已关闭(除了接收值外):
v, ok := <-ch
- 内置
len()
function tells the number of elements queued (unread); the builting cap()
函数returns通道缓冲容量
其他用途
有关更实际的示例,请参阅如何使用通道来实现 . A similar use is distributing values from a producer to consumer(s)。
另一个实际例子是实现一个.
另一个实际例子是。
通道通常用于使某些阻塞操作超时,利用 time.After()
which "fires" after the specified delay / duration ("fires" means a value will be sent on it). See this example for demonstration (try it on the 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 of the element type). Also know that even though a channel may be used for this kind of signalling, there's a better alternative for this: sync.WaitGroup
.
进一步阅读
了解信道公理以避免意外行为是值得的:
The Go Blog: Share Memory By Communicating
The Go Blog: Go Concurrency Patterns: Pipelines and cancellation
chan 是 Golang 中的一个频道。简单来说,你可以把它想象成一个盒子,你在其中一端放一个物品,然后从另一端拿起它。
无缓冲通道
缓冲通道
这是我写的小代码,方便大家了解频道。现在更改 go 例程的顺序并查看输出。每次输出可能不同。
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan int)
go func() {
time.Sleep(time.Second * 3)
messages <- 1
}()
go func() {
time.Sleep(time.Second * 2)
messages <- 2
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 3
}()
go func() {
for i := range messages {
fmt.Println(i)
}
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 4
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 5
}()
time.Sleep(time.Second * 5)
}
为了更好地理解,请访问此博客,其中在 GUI 中描述了 go 例程和通道。
为了更好的理解Go中channels的概念,可以先学习Go routines,然后阅读下面link,清楚的解释了golang中Channels的概念
https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb
请参阅此 link 以借助示例详细了解渠道。
这个概念与 Unix/Linux 从一开始就存在的东西非常相似:管道。
这些是语言内置的可靠的进程间/线程间通信工具。很方便。
在查看一些 Go 代码时,我发现了以下内容:
ch := make(chan int)
我在在线教程中查找了 Go Channels 的工作原理:
https://tour.golang.org/concurrency/2
但是我觉得这个例子不清楚。
谁能给我简单的解释和渠道使用的例子?
我认为规范对此非常清楚。 Spec: Channel types:
A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.
当您有多个并发执行的 goroutine 时,通道提供了允许 goroutine 相互通信的最简单方法。
一种通信方式是通过两个 goroutine 都可见的“共享”变量,但这需要适当的锁定/同步访问。
相反,Go 偏爱通道。引用自 Effective Go: Share by communicating:
Do not communicate by sharing memory; instead, share memory by communicating.
因此,您可以创建一个通道(对两个 goroutine 都可见),而不是将消息放入共享切片,而无需任何外部同步/锁定,一个 goroutine 可以通过通道发送消息(值),并且另一个 goroutine 可以接收它们。
Only one goroutine has access to the value at any given time. Data races cannot occur, by design.
所以事实上,任意数量的 goroutines 都可以在同一个通道上发送值,并且任意数量的 goroutines 都可以从中接收值,仍然不需要任何进一步的同步。有关详细信息,请参阅相关问题:
频道示例
让我们看一个例子,我们启动了 2 个额外的 goroutines 用于并发计算。我们将一个数字传递给第一个,将其加 1,并将结果传递到第二个通道。第 2 个 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)
}
通过通道进行通信还可以处理相互等待的 goroutines。在这个例子中,它意味着 MulBy10()
将等待 AddOne()
交付递增的数字,而 main()
将等待 MulBy10()
在打印结果之前。按预期输出(在 Go Playground 上尝试):
Result: 100
语言支持
有许多语言结构可以方便地使用频道,例如:
- 通道上的
for ... range
循环处理从通道接收的值,直到通道关闭。 select
语句可用于列出多个通道操作,例如在通道上发送和从通道接收, 可以不阻塞继续进行的将被选中(如果有多个可以继续进行则随机选择;如果 none 准备就绪将阻塞)。- 接收运算符有一种特殊形式,它允许您检查通道是否已关闭(除了接收值外):
v, ok := <-ch
- 内置
len()
function tells the number of elements queued (unread); the builtingcap()
函数returns通道缓冲容量
其他用途
有关更实际的示例,请参阅如何使用通道来实现
另一个实际例子是实现一个
另一个实际例子是
通道通常用于使某些阻塞操作超时,利用 time.After()
which "fires" after the specified delay / duration ("fires" means a value will be sent on it). See this example for demonstration (try it on the 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 of the element type). Also know that even though a channel may be used for this kind of signalling, there's a better alternative for this: sync.WaitGroup
.
进一步阅读
了解信道公理以避免意外行为是值得的:
The Go Blog: Share Memory By Communicating
The Go Blog: Go Concurrency Patterns: Pipelines and cancellation
chan 是 Golang 中的一个频道。简单来说,你可以把它想象成一个盒子,你在其中一端放一个物品,然后从另一端拿起它。
无缓冲通道
缓冲通道
这是我写的小代码,方便大家了解频道。现在更改 go 例程的顺序并查看输出。每次输出可能不同。
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan int)
go func() {
time.Sleep(time.Second * 3)
messages <- 1
}()
go func() {
time.Sleep(time.Second * 2)
messages <- 2
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 3
}()
go func() {
for i := range messages {
fmt.Println(i)
}
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 4
}()
go func() {
time.Sleep(time.Second * 1)
messages <- 5
}()
time.Sleep(time.Second * 5)
}
为了更好地理解,请访问此博客,其中在 GUI 中描述了 go 例程和通道。
为了更好的理解Go中channels的概念,可以先学习Go routines,然后阅读下面link,清楚的解释了golang中Channels的概念
https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb
请参阅此 link 以借助示例详细了解渠道。
这个概念与 Unix/Linux 从一开始就存在的东西非常相似:管道。
这些是语言内置的可靠的进程间/线程间通信工具。很方便。