channel 信道
在go中是一种特殊的类型,在任何时候,同时只能有一个goroutine访问通道进行发送和获取数据。goroutine间通过通道就可以通信。
channel类似队列先进先出
- channel本身是一个队列,先进先出
- 线程安全,不需要加锁
- 本身是有类型的,string,int等,如果要存多种类型,定义成interface类型
- channel是引用类型,必须make之后才能使用,一旦make,他的容量就确定了,不会动态增加,他跟map和slice不一样
特点
- 一旦初始化容量,就不会改变了。
- 当写满时,不可以写,取空时,不可以取
- 发送将持续阻塞直到数据被接收
把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。Go程序运行时能智能的发现一些永远无法发送成功的语句并做出提示 - 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止 - 每次接收一个元素
package main
import (
"fmt"
)
func main() {
//演示一下管道的使用
//1. 创建一个可以存放3个int类型的管道
var intChan chan int
intChan = make(chan int, 3)
//2. 看看intChan是什么
fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)
//3. 向管道写入数据
intChan <- 10
num := 211
intChan <- num
intChan <- 50
// //如果从channel取出数据后,可以继续放入
<-intChan
intChan <- 98 //注意点, 当我们给管写入数据时,不能超过其容量
//4. 看看管道的长度和cap(容量)
fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 3, 3
//5. 从管道中读取数据
var num2 int
num2 = <-intChan
fmt.Println("num2=", num2)
fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 2, 3
//6. 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
num3 := <-intChan
num4 := <-intChan
//num5 := <-intChan
fmt.Println("num3=", num3, "num4=", num4 /*, "num5=", num5*/)
}
注意接口类型的channel
package main
import (
"fmt"
)
type Cat struct {
Name string
Age int
}
func main() {
//定义一个存放任意数据类型的管道 3个数据
//var allChan chan interface{}
allChan := make(chan interface{}, 3)
allChan<- 10
allChan<- "tom jack"
cat := Cat{"小花猫", 4}
allChan<- cat
//我们希望获得到管道中的第三个元素,则先将前2个推出
<-allChan
<-allChan
newCat := <-allChan //从管道中取出的Cat是什么?
fmt.Printf("newCat=%T , newCat=%v\n", newCat, newCat)
//下面的写法是错误的!编译不通过
//fmt.Printf("newCat.Name=%v", newCat.Name)
//使用类型断言
a := newCat.(Cat)
fmt.Printf("newCat.Name=%v", a.Name)
}
channel的关闭:close()
关闭之后不能再写入,只能读。
只能由发送者执行这句代码
接收管道数据
- 阻塞接收数据,执行该语句时将会阻塞,直到接收到的数据并赋值给data变量。
例子:data := <-ch - 非阻塞接收数据(有问题,还是会报错deadlock)
例子:data,ok := <-ch
data: 表示接收到的数据。未接收到数据时,data为通道类型的零值
ok:表示是否接收到数据
非阻塞的通道接收方法可能造成高的CPU占用,因此使用非常少。如果需要实现接收超时检测,可以配合select和计时器channel进行。 - 接收任意数据,忽略接收的数据
例子:<-ch
执行该语句时将会发送阻塞,直到接收到数据,但接收的数据会被忽略。 - 循环接收
通道的数据接收可以借用for range语句进行多个元素的接收操作
例子:
for data := range ch{
}
通道ch是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过for遍历获得的变量只有一个(遍历管道之前要先关闭管道,不然会出现deadlock的错误)