1 不缓存的channel

以最简单方式调用make函数创建的时一个无缓存的channel,但是我们也可以指定第二个整形参数,对应channel的容量。如果channel的容量大于零,那么该channel就是带缓存的channel

ch = make(chan int)    // unbuffered channel
ch = make(chan int, 0) // unbuffered channel

一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句。
反之,如果接收操作先发生,那么接收者goroutine也将阻塞,
直到有另一个goroutine在相同的Channels上执行发送操作

func gosum(s []int, c chan int) {
    sum := 0

    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

    var si []int = []int{1, 2, 3, 4}
    chan_c := make(chan int, 0)

    go gosum(si, chan_c)

    rs := <-chan_c

    fmt.Println(rs)
 

2 缓存的channel

带缓存的Channel内部持有一个元素队列。通过缓存的使用,可以尽量避免阻塞,提供应用的性能。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的,比如

ch = make(chan int, 3) // buffered channel with capacity 3
我们可以在无阻塞的情况下连续向新创建的channel发送三个值
ch <- "A"
ch <- "B"
ch <- "C"
此刻,channel的内部缓存队列将是满的,如果有第四个发送操作将发生阻塞。channel的缓存队列解耦了接收和发送的goroutine

3 channel & Range

    c := make(chan int)
    go func() {
        for i := 0; i < 10; i = i + 1 {
            c <- i
            time.Sleep(1 * time.Second)
        }
        close(c)
    }()

    for i := range c {
        fmt.Println(i)
    }

    time.Sleep(100 * time.Second)

range c 产生的迭代值为Channel中发送的值,它会一直迭代知道channel被关闭。上面的例子中如果把 close(c) 注释掉,程序会一直阻塞在 for …… range 那一行
 

4 单向channel

Go语言的类型系统提供了单方向的channel类型,分别用于只发送或只接收的channel。类型chan<- int表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int表示一个只接收int的channel,只能接收不能发送。(箭头<-和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测。

因为关闭操作只用于断言不再向channel发送新的数据,所以只有在发送者所在的goroutine才会调用close函数,因此对一个只接收的channel调用close将是一个编译错误

func gosum(s []int, c chan<- int) {
    sum := 0

    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

5 channel&goroutines应用-批量查询数据库

func Query(conns []Conn, query string) Result {
    ch := make(chan Result, len(conns))  // buffered
    for _, conn := range conns {
        go func(c Conn) {
            ch <- c.DoQuery(query):
        }(conn)
    }
    return <-ch
}
 

6 select

select 语句选择一组可能的send操作和receive操作去处理。它类似 switch ,但是只是用来处理通讯(communication)操作。它的 case 可以是send语句,也可以是receive语句,亦或者 default 。receive 语句可以将值赋值给一个或者两个变量。它必须是一个receive操作。最多允许有一个 default case ,它可以放在case列表的任何位置,尽管我们大部分会将它放在最后。

如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。如果没有case需要处理,则会选择 default 去处理,如果 default case 存在的情况下。如果没有 default case ,则 select 语句会阻塞,直到某个case需要处理

select 语句和 switch 语句一样,它不是循环,它只会选择一个case来处理,如果想一直处理channel,你可以在外面加一个无限的for循环

 

分类:

技术点:

相关文章: