channel,可译为通道,是go语言协程goroutine之间的通信方式。
channel通信可以想象成从管道的一头塞进数据,从另一头读取数据。
协程通过channel通信可以不用进行加锁操作。
channel是拥有数据类型的,channel只能传递指定的数据类型的值。
语法:
c := make(chan 数据类型)
例子:
// 创建int类型的channel
c := make(chan int)
// 从channel变量c中读取数据,保存到变量v中
v := <-c
// 从channel变量c中读取数据,数据直接丢弃
<-c
提示:如果channel中没有数据,会阻塞协程,直到channel有数据或者channel关闭。
// 往channel变量c中,写入int数据100
c <- 100
channel有两种类型:
无缓冲的意思就是channel只能保存一个数据,当往无缓冲的channel写入第2个数据的时候协程会被阻塞,直到channel中的第1个数据被取走,才会唤醒被阻塞的协程。
缓冲channel,指的是channel中有一个缓冲队列,当写入的数据没有塞满这个缓冲队列之前,往channel写数据协程是不会被阻塞的,如果取数据的速度比写数据的速度快,那么永远不会阻塞写操作。
是否缓冲channel,写操作上区别就是什么时候会阻塞写操作。
package main
import "fmt"
// 定义累加函数,负责将s数组所有值相加, sum函数还接受一个int类型的channel参数c
func sum(s []int, c chan int) {
sum := 0
// 累加数组s的所有值
for _, v := range s {
sum += v
}
// 将计算的结果发生到channel中
c <- sum
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
// 定义一个int类型channel,用来接收协程计算结果
c := make(chan int)
// 创建第1协程,计算数组前半部分的累加值
go sum(s[:len(s)/2], c)
// 创建第2个协程,计算数组后半部分的累加值
go sum(s[len(s)/2:], c)
// 通过channel接收,两个协程的并发计算结果
// 这里读取两次channel
x := <-c
y := <-c
// 打印计算结果
fmt.Println(x, y, x+y)
}
运行结果如下:
-5 17 12
这个例子通过channel将子协程中的计算结果回传给主协程,根据channel的特性,如果子协程的计算还没有完成,不会给channel发送数据,主协程读取channel的操作会一直阻塞,直到收到数据为止。这样也就避免了子协程还没执行完,主协程就退出的问题。
package main
import (
"fmt"
"time"
)
func NoBufChannel() {
//创建一个无缓存的channel
ch := make(chan int, 0)
//len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
fmt.Printf("缓冲区剩余数据个数:%d, 缓冲区大小:%d\n", len(ch), cap(ch))
//新建协程
go func() {
for i := 0; i < 3; i++ { //写三次
fmt.Printf("准备写入第【%d】个子协程\n", i)
ch <- i //往chan写内容
//fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
fmt.Printf("上面是第【%d】个子协程的写入,我这里会执行到吗\n", i)
}
}()
//延时2秒
fmt.Printf("我要去睡两秒\n")
time.Sleep(2 * time.Second)
fmt.Printf("睡醒了\n")
for i := 0; i < 3; i++ { //必须读三次
fmt.Printf("此时开始读第【%d】个写入\n", i)
num := <-ch //读管道中内容,没有内容前,阻塞
fmt.Printf("第【%d】个写入读到了,值是【%d】\n", i, num)
}
}
func main() {
// 无缓冲通道
NoBufChannel()
}
运行结果如下:
缓冲区剩余数据个数:0, 缓冲区大小:0
我要去睡两秒
准备写入第【0】个子协程
睡醒了
此时开始读第【0】个写入
第【0】个写入读到了,值是【0】
此时开始读第【1】个写入
上面是第【0】个子协程的写入,我这里会执行到吗
准备写入第【1】个子协程
上面是第【1】个子协程的写入,我这里会执行到吗
准备写入第【2】个子协程
第【1】个写入读到了,值是【1】
此时开始读第【2】个写入
第【2】个写入读到了,值是【2】