什么是channel
channel是一个数据管道,可以往里面写数据,可以从里面读数据,channel是goroutine之间数据通信的桥梁,而且是线程安全的,channel遵循先进先出的原则,写入数据和读出数据都会加锁。
channel的分类
从读写的角度来区分
- 只读channel,单向channel
- 只写channel,单向channel
- 可读可写channel
从是否带有缓冲区来区分
- 带缓冲区的channel,定义了缓冲区的大小,可以存储多个数据
- 不带缓冲区的channel,只能存一个数据,并且只有当该数据被取出的时候才能存下一个数据
channel定义和声明
channel 按照声明和定义的方式可以从读写的方式上划分为只读的channel、只写的channel、可读可写的channel; 从带不带缓存区的角度又可以划分为带缓存区的channel和不带缓存区的channel。
readOnlyChan := make(<- chan int)
writeOnlyChan := make(chan <- int)
readAndWriteChan := make(chan int)
readOnlyChan := make(<- chan int,1)
writeOnlyChan := make(chan <- int,1)
readAndWriteChan := make(chan int,1)
对channel的操作方式
针对channel的操作有下面的几种方式,从channel 中读取数据、将数据写入channel、关闭channel
- 实践一
ch := make(chan int)
go func() {
ch <- 10
}()
fmt.Println(<-ch)
- 实践二
{
ch := make(chan int)
go store(ch)
for i := 0; i < 10; i++ {
fmt.Println("received ", <-ch)
}
}
func store(ch chan int) {
for i := 0; i < 10; i++ {
fmt.Println("send ", i)
ch <- i
}
}
- 实践三
// 带缓存的channel
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
实践四
{
s := []int{1, 2, 4, 5, 9}
ch := make(chan int)
go sums(s[:len(s)/2], ch)
go sums(s[len(s)/2:], ch)
x, y := <-ch, <-ch
fmt.Println(x, y, x+y)
}
func sums(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}
判断channel是否关闭
判断一个channel是否关闭的语法如下:如果ok为true则能够读到数据,说明channel没有关闭;如果ok为false,说明不能够读到数据,channel已关闭。
v, ok := <- ch
实践一
{
ch := make(chan int)
go test(ch)
for {
if v, ok := <-ch; ok {
fmt.Println("get value: ", v)
} else {
break
}
}
}
func test(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
channel超时处理
channel 可以结合time实现超时处理功能,当从channel中读取数据超过一定的时间的时候如果还没有数据到来,那么就可以认为当前的读取操作已经超时,防止goroutine一直处于阻塞状态。
// 读取ch中的数据,然后一直等待,直到超时后推出
{
ch := make(chan int)
quit := make(chan bool)
go func() {
for {
select {
case v := <-ch:
fmt.Println(v)
case <-time.After(time.Second * 3):
quit <- true
fmt.Println("timeout send notice")
return
}
}
}()
for i := 0; i < 4; i++ {
ch <- i
}
<-quit
fmt.Println("main exit")
}