之前的goroutine都是作用在函数上测试的,但很多时候各个函数之间需要相互传输数据,那么就需要用到channel通道。channel是安全的,不会发生死锁等问题。

goroutine和channel往往结合使用,channel连接了不同的goroutine,使其相互传输数据。


定义


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异常。


通道总结


点赞(482)

评论列表共有 0 条评论

立即
投稿
返回
顶部