1.什么是channel
channel的中文意思就是通道。在go中channel主要是用于协程之间的通信
channel可以在同一时间保证只有一个携程在操作,从而避免了数据的竞争,实现的同步操作。
2.channel使用
1.在go中,channel需要使用make函数进行内存分配。可以使用make函数,指定缓冲区的大小。
ch := make(chan int,6)
如果不指定缓冲区,则成为无缓冲区通道。该通道只有sender和receiver都准备好时,才能进行通信。
例如:
func pump(ch chan int) {
for i := 1;;i++ {
ch <- i
}
}
ch1 := make(chan int) // 无缓冲通道
go pump(ch1)
以上程序没有receiver,程序会报错。因为通道是没有缓冲区的。当sender发送一个数据的时候,必须有receiver接收数据,这样sender才能继续发送数据。类似于生产者->消费者问题
func pump(ch chan int) {
for i := 1;;i++ {
ch <- i
}
}
ch1 := make(chan int) // 无缓冲通道
go pump(ch1)
fmt.Println(<-ch1) // 这里只接收的到发送的第一个数据1。
2.发送、接收数据
根据 <- 的流向,来判断是向通道发送数据,还是读取通道中的数据
ch := make(chan int,1)
ch <- 10 // 向ch通道发送数据
ch = make(chan int,1)
ch <- 10
x := <- ch // 读取ch通道中的数据,并复制给x变量。
注意:
不能像已经满了的通道发送数据,否则会造成阻塞。
ch = make(chan int,2)
ch <- 10
ch <- 20
ch <- 30 // 通道缓冲区大小为2,已满,不能再发送数据
输出
该错误告诉我们,所有的线程都已经休眠。则没有一个线程在运行,发生死锁。
3.关闭通道
close(ch) // 使用该方法关闭通道
只有发送者可以关闭通道。
4.读取通道中的数据
for range 可以读取通道中的数据,直到通道关闭
for range
例子:
for v := range ch1 {
fmt.Printf("%v",v)
}
3.单通道
顾名思义,就是单向的通道,也就是只能接受或者发送的通道。
1.只能接收
var ch <- chan int
2.只能发送
var ch chan <- int
4. select
不同于switch,select只能用于io操作,一般配合channel使用。
例子:
select {
case msg1 := <-c1:
fmt.Println("c1 received:",msg1)
case msg2 := <-c2:
fmt.Println("c2 received",msg2)
default:
fmt.Println("No data received")
}
以上例子中,只要一个通道里有数据,就会执行case。
如果没有匹配的case,且没有default语句。那么select会阻塞,直到某一case可以执行
与switch的主要区别:
switch的case是顺序匹配执行的。而select是随机执行的。
select中表达式必须是和通道有关的操作。
------------ -------------- 补充-----------------------------------------------------------
5.关闭通道
目前是使用close方法来关闭通道。但是需要注意:
1.在关闭通道的前提下,如果通道中数据,那么返回的是true
ch := make(chan string,5)
go func() {
ch <- "a"
ch <- "b"
ch <- "c"
ch <- "d"
ch <- "e"
}()
time.Sleep(time.Second*10)
close(ch) // 关闭通道
v,ok := <-ch
fmt.Printf("%s,ok=%t\n",v,ok) // v = “a”,ok = true
把上面中的数据读取完毕,返回的是false
ch := make(chan string,5)
go func() {
ch <- "a"
ch <- "b"
ch <- "c"
ch <- "d"
ch <- "e"
}()
time.Sleep(time.Second*10)
close(ch) // 关闭通道
for i := 0; i < 5; i++ {
v,ok := <-ch
fmt.Printf("%s,ok=%t\n",v,ok)
}
v,ok := <-ch
fmt.Printf("%s,ok=%t\n",v,ok)
运行结果
目前来看,关闭的通道,在无数据的情况下,会返回false。否则是true。