Go 基础 4:channel拔高 和 select

先说这个buffered chan就是在创建chan的时候加上个chan buffer的大小

ch := make(chan string, 5)

基本的chan在send的时候如果没有来receive,send的也会block住。但是带buffer的就不同了,再send的时候,如果buffer没有满,send方不会被block住。receive的时候,再buffer不空的时候不会receiver不会block,这个和基本的chan没有啥区别。(chan如果被关闭了,这时候读receive是不会block的,但是可以用一个用另外一个bool来确定当前读成功了没有)

另外channel还可以做成单向的,只能读/写。

func writeOnly(ch chan<- string) {
    ch <- "hahaha"
    // v := <- ch // invalid

}

func readOnly(ch <-chan string) {
    for v := range ch {
        fmt.Println(v)
    }
}

func main() {
    ch1 := make(chan string)
    go writeOnly(ch1)
    readOnly(ch1)

}

有了chan,基本是配合select来用。

select {
        case cmd := <-ch1:
            // Handle ...
        case cmd := <-ch2:
            ...
        case cmd := <-chStop:
            // stop server
        }

如果所有的ch都没有数据,那这个select就会等在这里。如果有一个有,就会执行对应的case,并结束select模块。所以一般外面加个while。这个有点类似switch,但是目前是不支持fallthrough的。
另外支持default,如果所有的ch都没有数据准备好,就执行default。注意,如果进入一个case,不会再进来这个default。

下面是实例是两个生产者和一个消费者,消费者不是一直block,每隔一段时间醒一下,再实际应用中可以发送下status状态方便monitor。

func producer1(ch chan string) {
    for i := 0; i < 10; i++ {
        ch <- "1: " + strconv.FormatInt(int64(i), 10)
    }
}

func producer2(ch chan string) {
    for i := 0; i < 15; i++ {
        ch <- "2: " + strconv.FormatInt(int64(i), 10)
    }
}

func checker(ch chan string) {
    for {
        ch <- "wake up"
        time.Sleep(time.Duration(1) * time.Second)
    }
}

func consumer(ch1, ch2 chan string) {
    for {
        select {
        case v := <- ch1:
            fmt.Println(v)
        case v2 := <- ch2:
            fmt.Println(v2)
        default:
            fmt.Println("xxxx")
            time.Sleep(time.Duration(1) * time.Second)
        }
    }
}

func main() {
    ch1 := make(chan string, 3)
    ch2 := make(chan string, 3)
    go producer1(ch1)
    go producer2(ch2)
    consumer(ch1, ch2)
}
output:
xxxx
2: 0
1: 0
1: 1
2: 1
1: 2
1: 3
1: 4
1: 5
2: 2
2: 3
1: 6
1: 7
2: 4
1: 8
1: 9
2: 5
2: 6
2: 7
2: 8
2: 9
2: 10
2: 11
2: 12
2: 13
2: 14
xxxx
xxxx
xxxx
会一直下去。

但是这样的话,再default里面停等的时候即使ch1,ch2来了数据,也没有办法处理。所以我感觉这个default用法不应该是这样的。如果真的就是需要不断的unblock channel的话,可以另外用个ch来产生unblock的事件。像下面这样。

func producer1(ch chan string) {
    for i := 0; i < 10; i++ {
        ch <- "1: " + strconv.FormatInt(int64(i), 10)
    }
}

func producer2(ch chan string) {
    for i := 0; i < 15; i++ {
        ch <- "2: " + strconv.FormatInt(int64(i), 10)
    }
}

func checker(ch chan string) {
    for {
        ch <- "wake up"
        time.Sleep(time.Duration(2) * time.Second)
    }
}

func consumer(ch1, ch2, wake chan string) {
    for {
        select {
        case v := <- ch1:
            fmt.Println(v)
        case v2 := <- ch2:
            fmt.Println(v2)
        case v3 := <- wake:
            fmt.Println("xxxx", v3)
        }
    }
}

func main() {
    ch1 := make(chan string, 3)
    ch2 := make(chan string, 3)
    wake := make(chan string)
    go producer1(ch1)
    go producer2(ch2)
    go checker(wake)
    consumer(ch1, ch2, wake)
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容