转载请注名出处:https://blog.csdn.net/sublio/article/details/106478103
全系列目录:https://blog.csdn.net/sublio/article/details/106480267
通道类型:以通信作为手段来共享内存
Channel(注:引用类型,零值为nil不能直接用)
目录
声明和初始化
-
三种(后两种直接声明使用没有意义):chan T | chan<- T | chan-> T;其中T表示类型。
-
别名声明:type IntChan chan int
-
变量声明:var intChan chan int
-
缓冲通道初始化:make(chan int, 10)
-
len表示通道中已经存在的元素的个数,会有所变化。
-
cap表示最所容纳的元素个数,即上面的10。
-
-
非缓冲通道初始化:make(chan int, 0)或者make(chan int)
-
len和cap永远为0。
-
从通道中接收元素
-
两种方式:
-
elem := <- strChan:通道关闭且没有元素可以取出来的时候返回的是类型的零值,无法区分是真的传零值还是通道关闭。
-
elem, ok := <- strChan:通道关闭时且没有元素可以取出来的时候返回的是类型的零值和false(操作失败),可区分传零值和通道关闭。
-
-
<-右边可以是任务表达式,只要表达式的结果类型是通道类型就行了。
-
试图从一个未被初始化的通道(nil)里接收元素,会造成当前goroutine永久阻塞。
往通道中发送元素
-
strChan <- "a"
-
试图向一个已经关闭的通道发送数据,会立即引发一个运行时恐慌,即使发送操作正在因通道已满而被阻塞,为了避免这样的流程中断,可以结合select语句。
-
试图从一个未被初始化的通道(nil)里发送元素,也会造成当前goroutine永久阻塞。
其它
-
空结构体类型struct{}的变量不占用内存变量,且该类型的所有变量都拥有相同的内存地址,建议用于传递信号,除非需要传递更多的信息。
缓冲通道接收发送的底层细节
-
通道没有元素的时候,运行接收会被阻塞。
-
通道已经满的时候,运行发送会被阻塞。
-
因为channel被阻塞的goroutine无论是因为接收(channel里面没东西了)还是发送(channel里面满了),最早被阻塞的会被最先唤醒,且Go运行时系统每次只会唤醒一个goroutine。
-
通道的缓存队列属于环形队列。
-
发送到通道的值会被复制,从通道拿到的值也是副本,经过通道的值会被复制一次或者两次。
-
一次的情况:通道空了,有人在等着接收,这时候往通道发送的值会绕过本身的缓冲队列,直接复制给最早等待的那个接收方所持有的内存地址。
-
两次的情况:先复制到通道的缓存中的内存地址,被接受的时候再复制给接收方。
-
由于是副本,两端对值的修改不会影响到另一方的原值,但如果是引用类型,修改就会影响收发方持有的值。
-
-
把一个元素发送给channel的操作一定在从channel接收这个元素的操作之前,换句话说,在一个通道完全复制一个元素之前,任务人都不可能从它那里接收到这个元素的副本。
非缓冲通道接收发送的底层细节
-
往channel发送数据的操作会被阻塞,直到有人接收,接收方会先得到元素的副本,然后在唤醒发送方所在的goroutine之后返回,也就是说,这时候接收操作会比发送操作先完成。
-
接收channel数据的操作会被阻塞,直到有人往里面发送数据,发送操作会直接把元素值复制给接收方,然后在唤醒接收方所在的goroutine之后返回,也就是说,这时候发送操作会比接收操作先完成。
-
以同步的方式传递元素值,收发方的速度总是与满的那一方持平,因此想要用非缓冲通道进行异步发送,需要另行异步化。
关闭通道
-
colse(dataChan)
-
要在发送方关闭
-
关闭后如果channel内还有元素,并不会对接下来的接收产生影响
-
elem, ok := <- strChan返回的第二个bool值会在通道关闭且元素都取完之后返回false
-
注意!!!,对上面第四点的补充,如果close掉通道,再执行接受操作,无论调多少次都会直接返回类型零值和false(有两个返回值的时候)。
单向通道
-
声明一个单向通道实际上并没有意义(只发送不接受或者只接收不发送想想也知道没啥用)。
-
通过函数的参数类型或者返回值类型来限制(Go的语法糖)。
-
func(c chan<- int);传入双向通道,在函数里面调用c久只能发送。
-
func() (c <-chan int);返回双向通道,在函数外面里面调用c久只能接收。
-
-
利用函数进行单向转双向是不行的。
-
直接进行类型转换是不行的,不论单向转双向还是双向转单向。
-
反向操作会造成编译错误:接收发送channel和发送接收channel。
for遍历
-
for e := range ch {}
-
遍历直到该通道关闭且没有元素。
-
不能接受发送通道,会造成编译错误。
-
也不能是没有初始化的channel,会使得goroutine永久阻塞在range子句位置。
select语句
-
case后面只能是对channel的发送或者接收语句。
-
case右边的表达式会都先求值,顺序:从左到右,从上到下。
-
自上而下地判断每个case后面的发送或者接收语句是够可以立即执行(没有被阻塞),如果有多个case可以,系统通过一个伪随机算法选择一个。
-
default语句放置的位置没有关系,没有的话,又没有一个case满足条件,就会堵塞直到有一个case中的条件满足。
-
可以用break立即结束。
参考文档
《Go并发编程实战(第2版)》——郝林