Channel
channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题。可以把它看成管道,一端读,一端写
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步 goroutine 奉行通过通信来共享内存,而不是共享内存来通信。引⽤类型 channel可用于多个 goroutine 通讯。其内部实现了同步,确保并发安全。
定义channel的变量方式
make(chan Type) //等价于make(chan Type, 0)
make(chan Type, capacity)
channel的操作方式
channel <- value //写到channel里面
<- channel //从channel中读出 x := <-channel //从channel读出来赋值给x
默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock
c := make(chan int)
go func() {
defer fmt.Println("子协程结束")
fmt.Println("子协程正在运行……")
c <- 666 //666发送到c
}()
num := <-c //从c中接收数据,并赋值给num
fmt.Println("num = ", num)
fmt.Println("main协程结束")
无缓冲区Channel:
不会保存接受到的值
ch := make(chan int)
go func() {
for i:=0;i<5 ;i++ {
fmt.Println("Sub go i Write: " ,i)
ch <- i
}
}()
for i:=0;i<5 ;i++ {
num := <- ch
fmt.Println("Main go read i :", num)
}
}
有缓冲区Channel:
会保存接受到的值
ch := make(chan int ,5) //存满5元素之前不会堵塞
fmt.Println(len(ch))
fmt.Println(cap(ch))
go func() {
for i := 0;i<10;i++ {
ch <- i
len:= len(ch)
cap:= cap(ch)
fmt.Println("Sub go : ", " len: ",len, " cap: ", cap)
//fmt.Println(i)
}
}()
time.Sleep(time.Second *5)
for i:= 0 ;i< 8 ;i++ {
num := <-ch
fmt.Println("Main go : " ,num)
}
input//
Sub go : len: 1 cap: 5
Sub go : len: 2 cap: 5
Sub go : len: 3 cap: 5
Sub go : len: 4 cap: 5
Sub go : len: 5 cap: 5
Main go : 0
Main go : 1
Main go : 2
Main go : 3
Main go : 4
Main go : 5
Sub go : len: 5 cap: 5 // 输出的多了 是因为io操作的延迟所导致的
Sub go : len: 0 cap: 5
Sub go : len: 1 cap: 5
Sub go : len: 2 cap: 5
Sub go : len: 3 cap: 5
Main go : 6
Main go : 7
关闭Channel
如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多余的值可接收将是有用的,因为接收者可以停止不必要的接收等待。这可以通过内置的close函数来关闭channel实现。
ch := make(chan int)
go func() {
for i:= 0;i<5 ;i++ {
ch <- i
}
close(ch)
}()
for {
if num,flag := <- ch;flag == true {
fmt.Println("Channel Read data :" ,num)
}else {
break
}
}
l 关闭channel后,无法向channel 再发送数据(引发 panic 错误后导致接收立即返回零值);
l 关闭channel后,可以继续从channel接收数据;
可以使用range来迭代不断操作channel
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//把 close(c) 注释掉,程序会一直阻塞在 for data := range c 那一行
close(c)
}()
for data := range c {
fmt.Println(data)
}
fmt.Println("Finished")
单向channel
默认情况下,通道channel是双向的,也就是,既可以往里面发送数据也可以同里面接收数据。
但是,我们经常见一个通道作为参数进行传递而值希望对方是单向使用的,要么只让它发送数据,要么只让它接收数据,这时候我们可以指定通道的方向。
声明方式:
var ch1 chan int // ch1是一个正常的channel,是双向的
var ch2 chan<- float64 // ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读int数据
chan<- //表示数据进入管道,要把数据写进管道,对于调用者就是输出。
<-chan //表示数据从管道出来,对于调用者就是得到管道的数据,当然就是输入。
可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通 channel
ch := make(chan int)
var WriteCh chan <- int = ch
WriteCh <- 123
var ReadCh <- chan int = ch
num := <-ReadCh
fmt.Println(num)