package main import ( "fmt" ) func main() { // 构建一个通道 ch := make(chan int) // 开启一个并发匿名函数 go func() { fmt.Println("start goroutine") // 通过通道通知main的goroutine ch <- 0 fmt.Println("exit goroutine") }() fmt.Println("wait goroutine") // 等待匿名goroutine <-ch fmt.Println("all done") }
使用通道接收数据
通道接收同样使用<-
操作符,通道接收有如下特性:
① 通道的收发操作在不同的两个 goroutine 间进行。
由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
② 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
③ 每次接收一个元素。
通道一次只能接收一个数据元素。
通道的数据接收一共有以下 4 种写法。
1) 阻塞接收数据
阻塞模式接收数据时,将接收变量作为<-
操作符的左值,格式如下:
data := <-ch
执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
2) 非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
data, ok := <-ch
- data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
- ok:表示是否接收到数据。
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。
3) 接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式如下:
<-ch
执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。
通道结合rpc
package main import ( "errors" "fmt" "time" ) // 模拟RPC客户端的请求和接收消息封装 func RPCClient(ch chan string, req string) (string, error) { // 向服务器发送请求 ch <- req // 等待服务器返回 select { case ack := <-ch: // 接收到服务器返回数据 return ack, nil case <-time.After(time.Second): // 超时 return "", errors.New("Time out") } } //使用了 time 包提供的函数 After(),从字面意思看就是多少时间之后,其参数是 time 包的一个常量,time.Second 表示 1 秒。 //time.After 返回一个通道,这个通道在指定时间后,通过通道返回当前时间。 //RPCClient() 函数中,执行到 select 语句时,第 9 行和第 11 行的通道操作会同时开启。 //如果第 9 行的通道先返回,则执行第 10 行逻辑,表示正常接收到服务器数据;如果第 11 行的通道先返回,则执行第 12 行的逻辑,表示请求超时,返回错误。 // 模拟RPC服务器端接收客户端请求和回应 func RPCServer(ch chan string) { for { // 接收客户端请求 data := <-ch // 打印接收到的数据 fmt.Println("server received:", data) // 通过睡眠函数让程序执行阻塞2秒的任务 time.Sleep(time.Second * 2) // 反馈给客户端收到 ch <- "roger" } } func main() { // 创建一个无缓冲字符串通道 ch := make(chan string) // 并发执行服务器逻辑 go RPCServer(ch) // 客户端请求数据和接收数据 recv, err := RPCClient(ch, "hi") if err != nil { // 发生错误打印 fmt.Println(err) } else { // 正常接收到数据 fmt.Println("client received", recv) } }
关闭通道 通道可以访问但不能发送数据
// 创建一个整型的通道 ch := make(chan int) // 关闭通道 close(ch) // 打印通道的指针, 容量和长度 fmt.Printf("ptr:%p cap:%d len:%d\n", ch, cap(ch), len(ch)) // 给关闭的通道发送数据 ch <- 1
请发表评论