chan 通道,可以理解成管道.从一端读入,从另一端输出
1、定义通道的方式(主要是定义通道的类型变量,指定该通道内只能存放这一类型的数据): var 变量名 chan 变量类型 例如:var chanweb chan int
2、初始化通道(创建通道)通道的零值是nil demo := make(chan int) 当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用。 两个channels的比较可以使用==进行比较。如果两个channel内引用相同的对象,则返回真
3、一个channel有发送和接受两个主要操作,都是通信行为。
一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都使用<-
运算符。在发送语句中,<-
运算符分割channel和要发送的值。在接收语句中,<-
运算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的。
ch <- x // 一个发送语句 把X发送到ch中
x = <-ch // 赋值语句中的接收表达式 从ch 接收数据 并把值赋给 v
<-ch // 一个接收语句;结果被丢弃一个
Channel还支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导致panic异常。 对一个已经被close过的channel进行接收操作依然可以接受到之前已经成功发送的数据; 如果channel中已经没有数据的话将产生一个零值的数据。 使用内置的close函数就可以关闭一个channel:例如:
close(ch)
chan 中的缓存
不带缓冲区的channel
不带缓冲区的chan的缓冲区大小是0。 不带缓冲区的chan线程写入时会立马发生阻塞,直到有其他线程有对该chan执行接收操作且接收成功后,写入的进程才会解除阻塞。 不带缓冲区的chan线程接收时也会立马发生阻塞,直到有其他线程对该chan执行写入操作后,接收的线程才会解除阻塞。 例子:
ch := make(chan int)
go func() {
time.Sleep(time.Second*3)
fmt.Println("receive over")
<- ch
}()
ch<-1
fmt.Println("send over")
带缓存的Channels
带缓存的Channel内部持有一个元素队列。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的。
ch = make(chan string, 3)
向缓存Channel的发送操作就是向内部缓存队列的尾部插入原因,接收操作则是从队列的头部删除元素。 获取channel的有效长度可以使用内置函数len或者cap
管道### 串联的Channels
Channels也可以用于将多个goroutine连接在一起,一个Channel的输出作为下一个Channel的输入。这种串联的Channels就是所谓的管道(pipeline)
单方向的Channel
可以在函数中定义chan,只允许chan单方向的发送和接收。
类型chan<- int
表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int
表示一个只接收int的channel,只能接收不能发送。(箭头<-
和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测。
//单方向的chan
func counter(out chan <-int) {
for x:=1;x < 100;x++ {
out <- x
}
close(out)
}
func squarer(out chan <- int,in <-chan int ) {
for v:=range in{
out <- v * v
}
close(out)
}
func printer(in <-chan int) {
for v:=range in{
fmt.Println(v)
```
```