Golang学习笔记:通道Channel(以通信作为手段来共享内存)

长街暗渡 3月前   阅读数 44 0

转载请注名出处:https://blog.csdn.net/sublio/article/details/106478103

全系列目录:https://blog.csdn.net/sublio/article/details/106480267

通道类型:以通信作为手段来共享内存

Channel(注:引用类型,零值为nil不能直接用)

目录

Channel(注:引用类型,零值为nil不能直接用)

声明和初始化

从通道中接收元素

往通道中发送元素

其它

缓冲通道接收发送的底层细节

非缓冲通道接收发送的底层细节

关闭通道

单向通道

for遍历

select语句

参考文档


声明和初始化

  1. 三种(后两种直接声明使用没有意义):chan T | chan<- T | chan-> T;其中T表示类型。

  2. 别名声明:type IntChan chan int

  3. 变量声明:var intChan chan int

  4. 缓冲通道初始化:make(chan int, 10)

    1. len表示通道中已经存在的元素的个数,会有所变化。

    2. cap表示最所容纳的元素个数,即上面的10。

  5. 非缓冲通道初始化:make(chan int, 0)或者make(chan int)

    1. len和cap永远为0。

 

从通道中接收元素

  1. 两种方式:

    1. elem := <- strChan:通道关闭且没有元素可以取出来的时候返回的是类型的零值,无法区分是真的传零值还是通道关闭。

    2. elem, ok := <- strChan:通道关闭时且没有元素可以取出来的时候返回的是类型的零值和false(操作失败),可区分传零值和通道关闭。

  2. <-右边可以是任务表达式,只要表达式的结果类型是通道类型就行了。

  3. 试图从一个未被初始化的通道(nil)里接收元素,会造成当前goroutine永久阻塞。

往通道中发送元素

  1. strChan <- "a"

  2. 试图向一个已经关闭的通道发送数据,会立即引发一个运行时恐慌,即使发送操作正在因通道已满而被阻塞,为了避免这样的流程中断,可以结合select语句。

  3. 试图从一个未被初始化的通道(nil)里发送元素,也会造成当前goroutine永久阻塞。

其它

  1. 空结构体类型struct{}的变量不占用内存变量,且该类型的所有变量都拥有相同的内存地址,建议用于传递信号,除非需要传递更多的信息。

缓冲通道接收发送的底层细节

  1. 通道没有元素的时候,运行接收会被阻塞。

  2. 通道已经满的时候,运行发送会被阻塞。

  3. 因为channel被阻塞的goroutine无论是因为接收(channel里面没东西了)还是发送(channel里面满了),最早被阻塞的会被最先唤醒,且Go运行时系统每次只会唤醒一个goroutine。

  4. 通道的缓存队列属于环形队列。

  5. 发送到通道的值会被复制,从通道拿到的值也是副本,经过通道的值会被复制一次或者两次。

    1. 一次的情况:通道空了,有人在等着接收,这时候往通道发送的值会绕过本身的缓冲队列,直接复制给最早等待的那个接收方所持有的内存地址。

    2. 两次的情况:先复制到通道的缓存中的内存地址,被接受的时候再复制给接收方。

    3. 由于是副本,两端对值的修改不会影响到另一方的原值,但如果是引用类型,修改就会影响收发方持有的值。

  6. 把一个元素发送给channel的操作一定在从channel接收这个元素的操作之前,换句话说,在一个通道完全复制一个元素之前,任务人都不可能从它那里接收到这个元素的副本。

非缓冲通道接收发送的底层细节

  1. 往channel发送数据的操作会被阻塞,直到有人接收,接收方会先得到元素的副本,然后在唤醒发送方所在的goroutine之后返回,也就是说,这时候接收操作会比发送操作先完成。

  2. 接收channel数据的操作会被阻塞,直到有人往里面发送数据,发送操作会直接把元素值复制给接收方,然后在唤醒接收方所在的goroutine之后返回,也就是说,这时候发送操作会比接收操作先完成。

  3. 以同步的方式传递元素值,收发方的速度总是与满的那一方持平,因此想要用非缓冲通道进行异步发送,需要另行异步化。

关闭通道

  1. colse(dataChan)

  2. 要在发送方关闭

  3. 关闭后如果channel内还有元素,并不会对接下来的接收产生影响

  4. elem, ok := <- strChan返回的第二个bool值会在通道关闭且元素都取完之后返回false

 

单向通道

  1. 声明一个单向通道实际上并没有意义(只发送不接受或者只接收不发送想想也知道没啥用)。

  2. 通过函数的参数类型或者返回值类型来限制(Go的语法糖)。

    1. func(c chan<- int);传入双向通道,在函数里面调用c久只能发送。

    2. func() (c <-chan int);返回双向通道,在函数外面里面调用c久只能接收。

  3. 利用函数进行单向转双向是不行的。

  4. 直接进行类型转换是不行的,不论单向转双向还是双向转单向。

  5. 反向操作会造成编译错误:接收发送channel和发送接收channel。

 

for遍历

  1. for e := range ch {}

  2. 遍历直到该通道关闭且没有元素。

  3. 不能接受发送通道,会造成编译错误。

  4. 也不能是没有初始化的channel,会使得goroutine永久阻塞在range子句位置。

 

select语句

  1. case后面只能是对channel的发送或者接收语句。

  2. case右边的表达式会都先求值,顺序:从左到右,从上到下。

  3. 自上而下地判断每个case后面的发送或者接收语句是够可以立即执行(没有被阻塞),如果有多个case可以,系统通过一个伪随机算法选择一个。

  4. default语句放置的位置没有关系,没有的话,又没有一个case满足条件,就会堵塞直到有一个case中的条件满足。

  5. 可以用break立即结束。

参考文档

《Go并发编程实战(第2版)》——郝林


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: