zoukankan      html  css  js  c++  java
  • go语法:chan通道

    参考:

    https://www.jianshu.com/p/24ede9e90490(简书:由浅入深剖析go channel)

    https://zhuanlan.zhihu.com/p/32521576(知乎:go channel 最佳实践)

    channel内部结构

    每个channel内部实现都有三个队列

    1. 接收消息的协程队列。这个队列的结构是一个限定最大长度的链表,所有阻塞在channel的接收操作的协程都会被放在这个队列里。
    2. 发送消息的协程队列。这个队列的结构也是一个限定最大长度的链表。所有阻塞在channel的发送操作的协程也都会被放在这个队列里。
    3. 环形数据缓冲队列。这个环形数组的大小就是channel的容量。如果数组装满了,就表示channel满了,如果数组里一个值也没有,就表示channel是空的。对于一个阻塞型channel来说,它总是同时处于即满又空的状态。

    一个channel被所有使用它的协程所引用,也就是说,只要这两个装了协程的队列长度大于零,那么这个channel就永远不会被垃圾回收。另外,协程本身如果阻塞在channel的读写操作上,这个协程也永远不会被垃圾回收,即使这个channel只会被这一个协程所引用。

     

    channel的类型

    channel类型的零值形式称为空channel。一个非空channel类型必须通过make关键字进行创建。例如make(chan int, 10)将会创建出一个可以容纳10个int值的channel。第二个整形的参数值代表的就是channel可以容纳数据的大小,如果不提供这个参数值,那默认值就是零。

    var ch chan string; // nil channel
    ch := make(chan string); // zero channel
    ch := make(chan string, 10); // buffered channel

    channel里面的value buffer的容量也就是channel的容量。channel的容量为零表示这是一个阻塞型通道,非零表示缓冲型通道[非阻塞型通道]。

     

    channel简单规则表

    下标的活跃Channel表示即非空又非关闭的Channel,缓冲与非缓存一样

      空channel 已关闭channel 活跃channel
    close(ch) panic panic 成功
    ch  <-  v 阻塞 panic 成功或阻塞
    v,ok <- ch 阻塞 永不阻塞 成功或阻塞

    channel规则详细解释

    空channel

    1. 关闭一个空channel会导致当前goroutine引发panic
    2. 向一个空channel发送值会导致当前的goroutine阻塞
    3. 从一个空channel接收值也会导致当前的goroutine阻塞

    在空channel上的调用len和cap函数都统一返回零。

    已关闭的Channel

    1. 关闭一个已关闭的channel会引发panic
    2. 向一个已关闭的channel发送值会引发panic。当这种send操作处于select块里面的case语句上时,它会随时导致select语句引发panic。
    3. 从一个已关闭的channel上接收值既不会阻塞也不能panic,它一直能成功返回。只是返回的第二个值ok永远是false,表示接收到的v是在channel关闭之后拿到的,对应得值也是相应元素类型的零值。可以无限循环从已关闭的channel上接收值。

    活跃的Channel

    1. 关闭操作
      1. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。
      2. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。
      3. 一个已关闭的channel内部的缓冲数组可能不是空的,没有接收的这些值会导致channel对象永远不会被垃圾回收。
    2. 发送操作
      1. 如果是阻塞型channel,那就从channel的接收协程队列中移出第一个协程,然后把发送的值直接递给这个协程。
      2. 如果是阻塞型channel,并且channel的接收协程队列是空的,那么当前的协程将会阻塞,并进入到channel的发送协程队列里。
      3. 如果是缓冲型channel,并且缓冲数组里还有空间,那么将发送的值添加到数组最后,当前协程不阻塞。
      4. 如果是缓冲型channel,并且缓冲数组已经满了,那么当前的协程将会阻塞,并进入到channel的发送协程队列中。
    3. 接收操作
      1. 如果是缓冲型channel,并且缓冲数组有值,那么当前的协程不会阻塞,直接从数组中拿出第一个值。如果发送队列非空,还需要将队列中的第一个goroutine唤醒。
      2. 如果是阻塞型channel,并且发送队列非空的话,那么唤醒发送队列第一个协程,该协程会将发送的值直接递给接收的协程。
      3. 如果是缓冲型channel,并且缓冲数组为空,或者是阻塞型channel,并且发送协程队列为空,那么当前协程将会阻塞,并加入到channel的接收协程队列中。

     

    总结

    根据以上规则,我们可以得出以下结论

    1. 如果channel关闭了,那么它的接收和发送协程队列必然空了,但是它的缓冲数组可能还没有空。
    2. channel的接收协程队列和缓冲数组,同一个时间必然有一个是空的
    3. channel的缓冲数组如果未满,那么它的发送协程队列必然是空的
    4. 对于缓冲型channel,同一时间它的接收和发送协程队列,必然有一个是空的
    5. 对于非缓冲型channel,一般来说同一时间它的接收和发送协程队列,也必然有一个是空的,但是有一个例外,那就是当它的发送操作和接收操作在同一个select块里出现的时候,两个队列都不是空的。

     

     

     

  • 相关阅读:
    c++作业2 9.22
    c++作业1 9.22
    c++练习题2
    c++练习题1
    10.10作业3
    10.10作业2
    10.10作业 1
    9.22作业5
    9.22作业4
    9.22zuo
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/15211438.html
Copyright © 2011-2022 走看看