定义
channel是一个引用类型,类似队列,遵循的是先进先出规则,声明格式:var username chan type。
如以下示例:
// 声明一个传递整形的通道
var ch1 chan int
// 声明一个传递int切片的通道
var ch2 chan []int
创建
channel声明后需要进行make初始化才可使用,不声明则为空值nil。
func main() { var ch chan int fmt.Println(ch) // nil ch1 := make(chan int, 3) fmt.Println(ch1) // 0xxxxxxxx }
channel有三种操作,分别是发送、接收、关闭。发送和接收都使用<-符号。
例如有一个ch通道。
使用ch通道发送数据为:ch <- 10
接收ch通道发出的数据为:a := <- ch,如果要忽略通道发出的数据,则什么都不写即可:<- ch
关闭ch通道:close(ch)
关闭通道需要注意:只有告诉接收方所有的数据都发送完毕,才需关闭通道,而通道是可以被垃圾回收机制回收的,所以不关也可以。
示例:
func main() { ch := make(chan int, 3) // 这时ch通道没有任何数据,所以接收时会报deadlock错误 a := <-ch fmt.Println(a) ch <- 10 b := <-ch fmt.Println(b) // 10 }
无缓冲区通道
无缓冲区通道即初始化时没有指定该通道的容量大小。
func main() { ch := make(chan int) ch <- 10 fmt.Println("success") }
上面就是一个无缓冲区通道的例子,make时没有定义通道大小,运行时会报deadlock错误,因为无缓冲区通道无法发送数据,所以程序会阻塞在ch <- 10这一行,造成死锁。
解决办法就是通过启用goroutine去接收值,示例如下:
func recv(c chan int) { ret := <-c fmt.Println("receive success", ret) } func main() { ch := make(chan int) go recv(ch) ch <- 10 fmt.Println("send success") }
注意:当从无缓冲区通道接受值时,该通道必须有值再发送。同理,发送值时,也必须有接收的。缺一不可,所以无缓冲区通道也叫同步通道,发送和接收是同步进行的。
有缓冲区通道
make初始化给了容量大小就是有缓冲区通道,可以使用len查看通道目前存有几个元素,cap查看通道的容量。
func main() { ch := make(chan int, 3) ch <- 10 ch <- 20 fmt.Println(len(ch)) // 2 fmt.Println(cap(ch)) // 3 fmt.Println(<-ch) // 10 fmt.Println(<-ch) // 20 fmt.Println(len(ch)) }
通道遍历
通道遍历可以使用range,如下示例:
func main() { ch := make(chan int, 10) for i := 0; i < 10; i++ { ch <- i } close(ch) for v := range ch { fmt.Println(v) } }
注意:ch通道在发送值后,使用close关闭了,如果此处不关闭,下面range遍历时就会报死锁,因为循环接收值时,接收完后,还会循环取,这时已经没值了,就会不断循环造成死锁,所以需要进行关闭。
单向通道
函数中允许参数为通道类型,但有时候可能只需要该通道来接收值或者发送值,这时就需要对该通道设置单向设置。设置时只需要在函数参数上添加<-符号即可。
// a函数的通道参数只能传入值,即只写的单向通道 func a(ch chan<- int) { fmt.Println(ch) } // b函数的通道参数只能输出值,即只读的单向通道 func b(ch <-chan int) { fmt.Println(ch) }
示例如下:
func onlyWrite(ch chan<- int) { for i := 0; i < 10; i++ { ch <- i } close(ch) } func onlyRead(read <-chan int, write chan<- int) { for v := range read { write <- v } close(write) } func chPrint(ch <-chan int) { for v := range ch { fmt.Println(v) } } func main() { ch1 := make(chan int) ch2 := make(chan int) go onlyWrite(ch1) go onlyRead(ch1, ch2) chPrint(ch2) }
通道关闭的特点
1,通道关闭后,再发送值会导致panic异常。
2,通道关闭后,再接收值会一直进行数据的读取,直到读完。
3,通道关闭后,再进行关闭会导致panic异常。
通道总结
发表评论 取消回复