golang channel & select

作者: 大漠狼道 | 来源:发表于2015-11-02 00:51 被阅读2384次

    通过消息来共享数据是golang的一种设计哲学,channel则是这种哲理的体现.
    channel定义

    var varName chan dataType
    

    dataType非常广泛,可以是基本的string,int等,也可以是map,slice,自定义的type类型,甚至可以是channel。类型非常丰富,因而在golang中很容易做到通过消息来共享数据。

    初始化

    channel通过make来初始化;未初始化的channel为nil(刚接触的会常常碰到因未初始化而导致的死锁问题).
    channel通过箭头<-反向来初始化为单向通道,即只读"<- chanName"、只写"<- chanName",省略箭头表示双通道,默认为双通道.
    当buffer size为0或者缺失时,这时我们称之为unbuffered;反之则为buffered channel.

    阻塞条件

    • 未初始化的channel,一直会阻塞
    • unbuffered channel: 读和写同时准备好了,channel才会开始通信,只有读或者写,则会一直阻塞.
    • buffered channel里面写数据时,直到写满为止才会阻塞,而读则是直到为channel为空时才会阻塞.

    不带buffer的channel很容易实现顺序执行、同步等;而带buffer的channel通常用来处理异步事件,只要往里面一扔就撤.

    关闭channel

    可以用close来关闭channel,但是close后,里面的数据还是会存在,直到被全部消费掉,而已再有些释放资源的地方,close channel后,通过len(channel)来判断剩余资源,并做相应的处理.
    对了,这里还有个小细节:

    v,ok := <- chan
    

    很多地方都会说通过上面那种方式来判断channel是否关闭.其实这种说法会带个小坑.如果是buffer channel,close()后,如果里面还有数据的话,上面那种方式还是能取到数据,ok也是为true,什么时候会获取不到呢?那就是里面的数据全都消耗完了之后,ok为false.
    因而上面的表达式表述为如下更合理些:

    v,ok:=<-chan,用来表述为能否从channel获取数据,如果能取到,ok为true,如果取不到ok为false,同时channel已关闭或不存在.

    有个比较常用,但新手会觉得奇怪的地方就是:

    chan <- struct{}{}
    

    语义为将一个空数据传递给channel.因为struct{}{}占用的内存非常小,而且我们对数据内容也不关心,通常用来做信号量来处理,例如结束某个service等.

    select

    表达式:

    select{
    case :
    /*...*/
    default:
    /*...*/
    }
    

    通常用在需要处理多个channel的地方.select会一直堵塞,直到某个case收到消息后,但是如果有default case的话,其他case没收到消息的话,会马上走default case,然后整个select语句结束.

    for ... select

    通常用for ... select语句来循环处理消息,有两个点需要稍微注意下.
    1.�break可以用于select语句,因而在break语句里面是不能中断整个for循环的.那想跳出for循环怎么办呢?用标签:

    LOOP:
        for{
            select{
                case ....:
                    break LOOP  //@A
            }
          }
    
    注意上面@A的地方
    

    2.select语句中是阻塞的,也就是说,直到选中的case执行完了之后,才会进入下一个循环.因而需要耗时的事情不要放在里面处理,可以单独开个go routine来处理.

    相关文章

      网友评论

        本文标题:golang channel & select

        本文链接:https://www.haomeiwen.com/subject/lflqhttx.html