go中channel是一种重要的引用数据类型,可以看作是管道,可以通过并发的核心单元发送或者接收数据进行通讯。
操作符 <-
,箭头指向,即数据的流向,没有指明方向,那么Channel就是双向的,既可以接收数据,也可以发送数据
ch <- v // 发送值到Channel ch中
v := <-ch // 从Channel ch中接收数据,并赋值给v
// 初始化 Channel ch, 类似于Slice,map的数据类型一样,channel必须先创建,然后再使用
ch := make(chan int)
Channel的方向
- 不确定双向性、指定唯一单向
chan T // 既可以接收数据,也可以发送数据
chan<- float64 // 只可以用来发送 float64 类型的数据
<-chan int // 只可以接收 float64 数据类型的数据
<-总是优先和最左边的类型结合
chan<- chan int // 等价 chan<- (chan int)
chan<- <-chan int // 等价 chan<- (<-chan int)
<-chan <-chan int // 等价 <-chan (<-chan int)
chan (<-chan int)
设置Channel的容量,代表Channel缓存的大小
make初始化的时候可以设置 如果没有设置Channel的大小,或者设置为0,那么默认Channel的没有缓存的,当接收者和发送者都准备好了后,他们通讯才会发生阻塞(blocking) 如果我们设置了缓存的大小,那么就有可能不会发送阻塞了,只有buffer满了后,发送者才会阻塞,只有清空了缓存,接收者才会阻塞。 一个nil的Channel不会通讯。
make(chan int, 100)
关闭Channel
内置的方法close()
可以关闭Channel,如何检测Channel是否被关闭
defer close(c) // 关闭 channel
v, ok := <-ch // 检测 channel是否被关闭
select的使用,类似于switch
case
用可能处理的接收语句,有可能是发送语句,还有可能是默认 default
timeout 超时处理
如果没有case需要处理,select语句就会一直阻塞着
import "time"
import "fmt"
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
}
其实使用time.After
函数,它是返回一个<-chan Time
的单向channel,在指定时间发送一个当前时间给返回的channel中
同步
channel可以用在goroutine之间的同步
import (
"fmt"
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任务已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任务完成
<-done
}
生产者和消费者
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("show channel")
queue := make(chan int, 1)
go test.Producer(queue)
go test.Consumer(queue)
time.Sleep(1e9)
}
func Producer(queue chan<- int) {
for i := 0; i < 10; i++ {
fmt.Println("send:", i)
queue<- i+10000
}
}
func Consumer(queue <-chan int) {
for i := 0; i < 10; i++ {
v := <-queue
fmt.Println("receive:", v)
}
}