在上一篇文章《Go 并发机制:Goroutine》中,我们讨论了 Go 如何使用 goroutine 实现并发。本文我们继续讨论 goroutine 如何利用 channel 来进行通信。
什么是 channel
Channel,通道,可看作是 goroutine 进行通信的管道(pipe)。跟水可以在管道中从一端流向另一端类似,数据也可以通过通道从一端发送,从另一端接收。
声明 channel
每一个通道都有一个具体的类型,即这个通道允许传输的数据类型。
ch T
一个类型为 T 的通道。
通道的零值为 nil,可以使用 make 来定义通道。例如:
package main
import "fmt"
func main() {
var a chan int
if a == nil {
fmt.Println("channel a is nil, going to define it")
a = make(chan int)
fmt.Printf("Type of a is %T", a)
}
}
上面的程序中,首先声明了 a 通道,且a 通道的类型为 int。由于 a 通道开始的值为 nil,接下来进行通道的定义。
运行程序,输出:
channel a is nil, going to define it
Type of a is chan int
通常来说,也可以使用短变量声明来定义一个通道:
a := make(chan int)
上面的代码,同样定义了一个类型为 int 的通道。
通过 channel 收发数据
可以通过通道收发数据,例如:
data := <- a // 从通道 a 接收数据
a <- data // 往通道 a 发送数据
通过通道发送和接收数据都使用 <- 操作符。第一行中,箭头指向变量 data,表示从通道中接收数据,并保存至变量 data 中。
第二行中,箭头指向通道 a,表示往通道 a 发送数据。
默认情况下,从通道接收数据和往通道发送数据都会阻塞。对于发送方来说,发送数据将会一直阻塞,直到另一个 goroutine 从通道读取数据。同理,对于接收方来说,接收数据将会一直阻塞,直到另一个 goroutine 往通道发送数据。
通道的这种特性有利于 goroutine 在不显式使用锁或者条件变量的情况进行有效的通信。
例子
我们继续以上一篇文章《Go 并发机制:Goroutine》中的程序为例,改用通道来实现。
为便于对照,这里附上原程序:
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
为避免 main goroutine 没有等待 hello goroutine 执行完毕就直接退出,main goroutine 使用了 Sleep 睡眠以等待 1 秒时间。如何改用通道更优雅地实现类似的功能呢?
package main
import (
"fmt"
)
func hello(done chan bool) {
fmt.Println(<

最低0.47元/天 解锁文章
506

被折叠的 条评论
为什么被折叠?



