Golang Series
Hello GoLang: https://blog.yexca.net/archives/154
GoLang (var and const) 变量与常量: https://blog.yexca.net/archives/155
GoLang (func) 函数: https://blog.yexca.net/archives/156
GoLang (slice and map) 切片: https://blog.yexca.net/archives/160
GoLang (OOP) 面向对象: https://blog.yexca.net/archives/162
GoLang (reflect) 反射: https://blog.yexca.net/archives/204
GoLang (struct tag) 结构体标签: https://blog.yexca.net/archives/205
GoLang (goroutine) go 程: https://blog.yexca.net/archives/206
GoLang (channel) 通道: This Page
Go 程 (goroutine) 可以通过 channel 进行传递数据,引用类型 channel 可用于多个 goroutine 通讯,其内部实现了同步,确保并发安全
有点类似 RabbitMQ (仅个人为方便学习所类比,实则为不同东西)
定义变量
channel 为引用类型,复制或函数调用时将引用同一个 channel 对象,零值为 nil
通过 make() 函数创建,例如
c := make(chan int)
// 添加容量为3
c := make(chan int, 3)
当容量为 0 时,channel 是无缓冲阻塞读写的,大于 0 时有缓存是非阻塞的,直至写满才阻塞
通过 <-
来接收和发送数据
// 发送数据到 channel
channel <- 3
// 接收并丢弃
<-channel // 注意无空格
// 接收并赋值给变量
x := <-channel
// 接收并赋值给变量,并判断是否接收成功(channel 是否为空)
data, flag := <-channel
无缓冲
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
defer fmt.Println("A.defer")
c <- 6
fmt.Println("A running")
}()
num := <-c
fmt.Println("num =", num)
fmt.Println("main over")
}
有缓冲
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 3)
go func() {
defer fmt.Println("A over")
for i := 0; i < 3; i++ {
c <- i
fmt.Println("A goroutine, i =", i, "len =", len(c), "cap =", cap(c))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
num := <-c
fmt.Println("main, num =", num)
}
fmt.Println("main over")
}
关闭 channel
通过 close()
关闭
package main
import "fmt"
func main() {
c := make(chan int, 5)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
// 关闭
close(c)
}()
for {
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("Main Finished")
}
使用 range
上述 main 的 for 循环可以简写使用 range
package main
import "fmt"
func main() {
c := make(chan int, 5)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
close(c)
}()
//for {
// if data, ok := <-c; ok {
// fmt.Println(data)
// } else {
// break
// }
//}
for data := range c {
fmt.Println(data)
}
fmt.Println("Main Finished")
}
单向 channel
默认情况下 channel 是双向的,即可读可写,也可以指定通道方向,只读或只写
var c chan int // 声明正常双向 channel
// c1 只可写
var c1 chan<- int
// c2 只可读
var c2 <-chan int
可以把双向 channel 转为单向,反之不可。也就是可以定义函数形参为单向,但传递双向
// chan<- //只写
func counter(out chan<- int) {
defer close(out)
for i := 0; i < 5; i++ {
out <- i //如果对方不读 会阻塞
}
}
// <-chan //只读
func printer(in <-chan int) {
for num := range in {
fmt.Println(num)
}
}
func main() {
c := make(chan int) //双向
go counter(c) //生产者
printer(c) //消费者
fmt.Println("done")
}
select
select 可以监听多个 channel 上的数据流动,语法与 switch 类似,但每个 case 语句里必须是一个 IO 操作
一般放到 for{}
语句块中
package main
import "fmt"
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
// main
x, y := 1, 1
for {
select {
case c <- x:
tmp := x
x = y
y = tmp + y
case <-quit:
fmt.Println("quit")
return
// 可以有 default,此例不需要
}
}
}