Go Channel
最后修改时间:2025 年 5 月 3 日
在本文中,我们将展示如何在 Golang 中使用 channels。
一个 goroutine 是 Go 中的一个轻量级执行线程,能够实现函数的并发执行。与传统线程不同,goroutine 非常高效且开销极小,这使得它们非常适合处理并发任务,而不会造成过度的资源消耗。
Goroutines 独立运行,但共享相同的地址空间,这允许它们之间进行高效的通信。由于它们与其他正在运行的代码并行执行,因此它们使开发人员能够同时执行多个操作,例如处理请求、处理 I/O 操作或管理后台计算。
为了实现 goroutines 之间安全同步的通信,Go 提供了 channels。Channels 作为数据交换的管道,允许 goroutines 以结构化的方式发送和接收信息。通过使用 channels,开发人员可以协调执行、防止竞态条件,并确保并发函数之间平滑的数据传输。
通过 goroutines 和 channels 的组合,Go 实现了高效的并发管理,使其成为处理可伸缩、高性能应用程序的强大语言。
Channel 函数和运算符
在本节中,我们将介绍与 channels 相关的函数和运算符。
c := make(chan int)
Channel 使用 make
函数和 chan
关键字创建。Channel 会被赋予一个类型。
<-
运算符用于读取 channel 和写入 channel。
c <- v
这行代码将一个值写入 channel。
<- c
这行代码从 channel 读取一个值。
close(c)
Channel 使用 close
函数关闭。
for e := range(c) { fmt.Println(e) }
我们可以使用 range
遍历从 channel 发送的值。
len(c)
len
函数返回已成功发送到 channel 但尚未取出的元素的数量。
单向 Channels
默认情况下,channels 是双向的。它们都可以用于发送和接收数据。
rc chan<- int
将箭头运算符放在 chan
关键字的右侧,我们创建了一个只能接收的 channel。
wc <-chan int
将箭头运算符放在 chan
关键字的左侧,我们创建了一个只能发送的 channel。
Go Channel 简单示例
以下是一个使用 channel 的简单示例。
package main import "fmt" func main() { c := make(chan string) go func() { c <- "an old falcon" }() msg := <-c fmt.Println(msg) }
在程序中,我们有两个 goroutines。一个使用 go
关键字创建;主函数也是一个 goroutine。
c := make(chan string)
我们创建一个 string 类型的 channel。
go func() { c <- "an old falcon" }()
在匿名的 goroutine 中,我们将一个 string 消息发送到 channel。
msg := <-c
在主 goroutine 中,我们从 channel 读取值。
$ go run main.go an old falcon
Go Channel 死锁
当代码在使用 channel 时被阻塞时,就会发生 channel 死锁。当我们尝试从一个打开的空 channel 读取,或者尝试向一个打开的满 channel 写入时,channel 就会被阻塞。
package main import "fmt" func main() { c := make(chan string) c <- "an old falcon" msg := <-c fmt.Println(msg) }
此程序以死锁结束。
c <- "an old falcon"
我们将一个值写入 channel。channel 会阻塞并等待直到它被清空。因此,读取 channel 并清空它的那一行代码将无法到达。这种情况会导致死锁。
$ go run main.go fatal error: all goroutines are asleep - deadlock! ...
Go Channel Range
我们可以使用 range 关键字遍历发送到 channel 的数据。
package main import ( "fmt" "time" ) func main() { c := make(chan string) go hello("Martin", c) for msg := range c { fmt.Println(msg) } } func hello(name string, c chan string) { for i := 0; i < 5; i++ { msg := fmt.Sprintf("Hello %s!", name) c <- msg time.Sleep(time.Millisecond * 500) } close(c) }
在程序中,我们将五个消息发送到一个 channel。
go hello("Martin", c)
使用 go
创建一个 hello
goroutine。我们将 channel 作为参数传递。
for msg := range c { fmt.Println(msg) }
使用 range
关键字,我们遍历消息并将其打印到控制台。
func hello(name string, c chan string) { for i := 0; i < 5; i++ { msg := fmt.Sprintf("Hello %s!", name) c <- msg time.Sleep(time.Millisecond * 500) } close(c) }
在 hello
函数中,我们创建了五个消息并通过 channel 发送给 main
goroutine。当 goroutine 完成后,我们使用 close
关闭 channel。
$ go run main.go Hello Martin! Hello Martin! Hello Martin! Hello Martin! Hello Martin!
单向 Channel 示例
在以下示例中,我们使用单向 channels。
package main import "fmt" func main() { c1 := make(chan int) c2 := make(chan int) go power(c2, c1) go power(c2, c1) go power(c2, c1) c2 <- 2 fmt.Println(<-c1) c2 <- 4 fmt.Println(<-c1) c2 <- 5 fmt.Println(<-c1) } func power(wc <-chan int, rc chan<- int) { num := <-wc res := num * num rc <- res }
在程序中,我们有一个 power
函数,它接受一个只写 wc
channel 和一个只读 rc
channel。
$ go run main.go 4 16 25
来源
The Go Programming Language Specification
在本文中,我们学习了 Go channels。