go channel 的使用场景

概述

go channel 存在3种状态

  • nil 未初始化的状态,只进行了声明
  • active 正常的channel,可读或者可写
  • closed 已关闭,关闭后的channel != nil
image.png

有种特殊情况,当nil的通道在select的某个case中时,这个case会阻塞,但不会造成死锁。

for...range

不断从channel中读取数据,for...range可以自动识别close掉的channel

func main() {
    c := make(chan int, 3)
    wg := sync.WaitGroup{}

    wg.Add(2)

    go func() {
        defer wg.Done()
        for x := range c {
            fmt.Println(x)
        }
    }()

    go func() {
        defer wg.Done()
        for i := 4; i > 0; i-- {
            c <- i
        }
        close(c)
        fmt.Println(c == nil)
    }()

    wg.Wait()
}

// output
4
3
2
1
false

_, ok

对于已关闭的channel进行读取,会得到零值,这样会造成读取到的假象,通过_, ok可以判断是否从channel中读取到数据

func main() {
    c := make(chan bool, 3)
    close(c)
    x, ok := <- c
    fmt.Println(x, ok)
}

// output 
false false

select

select可同时监控多路channel,并处理最先发生的channel

var wg sync.WaitGroup

func main() {
    c := make(chan int, 2)
    d := make(chan string, 2)
    wg.Add(1)

    go func() {
        defer wg.Done()
        d<-"joker"
        c<-1
    }()

    select {
    case x := <-c:
        fmt.Println(x)
    case y := <-d:
        fmt.Println(y)
    }

    wg.Wait()
}

// output 
joker

只读/只写channel

只读channel只能读取,只写channel只能写入,更易读,更安全

func main() {
    c := make(chan int, 2)
    cw := (chan<- int)(c)
    cr := (<-chan int)(c)

    cw <- 1
    cw <- 2
    fmt.Println(<-cr,<-cr)
}

// output 
1 2

无阻塞读写channel

通过select和time.After配合读写无阻塞

func main() {
    c := make(chan interface{})
    v, ok := unBlockRead(c)
    fmt.Println(v, ok)

    ok = unBlockWrite(c, "joker")
    fmt.Println(ok)
}

func unBlockWrite(c chan<- interface{}, v interface{}) (ok bool) {
    select {
    case c<-v:
        return true
    case <-time.After(time.Second):
        return false
    }
}

func unBlockRead(c <-chan interface{}) (v interface{}, ok bool) {
    select {
    case v, ok = <-c:
        return
    case <-time.After(time.Second):
        return nil, false
    }
}

// output
 false
false

你可能感兴趣的:(go channel 的使用场景)