go语言之channel

  1. 1 channel定义
  2. 2 channel创建
  3. 3 channel关闭
  4. 4 channel特性
  5. 5 range遍历
  6. 6 select
  7. 7 timer和ticker
  8. 8 同步

1 channel定义

channel是Go中的一个核心类型,是Goroutine之间的通信方式,操作符:<-;channel有三种类型定义(chan|chan <-|<-chan),<-代表channel方向,如没有指定方向,则channel是双向的,既可以接收数据也可以发送数据:

chan T   //可以接收和发送T类型数据
chan<- float64  //只可以发送float64类型的数据
<-chan int //只可以用来接收int类型的数据

<-总是优先和最左边的类型结合:

chan<-chan int  //等价chan <- (chan int)
chan<- <-chan int // 等价 chan <- (<-chan int)
<-chan<-chan int  // 等价 <- chan (<-chan int)

2 channel创建

channel须先创建再使用:

ch := make(chan int)

创建带Buffer的channel:

ch := make(chan int, 100)

如果没有设置buffer数量,或者buffer为0,则channel没有缓存,此时只有sender和receiver都准备好了通信才会发生,否则阻塞;如果设置了buffer,则buffer满后send才会阻塞,buffer空后receive才会阻塞;

3 channel关闭

close方法可以关闭channel;
检查channel是否已经关闭的方法:
v, ok := <-ch
close调用之后继续从channel读取,如果buffer中还有数据未读完,则v返回数据,ok为true;如果buffer中数据已读完,则v返回零值,ok为false;

操作 nil channel closed channel not-closed non-nil channel
close panic panic 成功close
写ch <- 一直阻塞 panic 阻塞或成功写入数据
读<- ch 一直阻塞 读取对应类型零值 阻塞或成功读取数据

关闭channel会产生一个广播机制,所有从channel读取消息的goroutine都会收到消息
For a channel c, the built-in function close(c) records that no more values will be sent on the channel. It is an error if c is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.

4 channel特性

1) 线程安全;
2) 可以作为一个FIFO队列,接收和发送的数据顺序一致;
3) 对nil channel读写会永久block;
4) 写closed channel会panic;
5) 从closed channel读会立即返回,接收完buffer数据后返回零值;

5 range遍历

func main() {
    len := 10
    ch := make(chan int, 10)
    for i:=1; i<=len; i++{
        ch <- i
    }

    go func(ch chan int) {
        time.Sleep(time.Duration(5) * time.Second)
        fmt.Println("close channel")
        close(ch)
    }(ch)

    for v := range ch {
        fmt.Println("chan val:", v)
        time.Sleep(time.Duration(1) * time.Second)
    }

    fmt.Println("main goroutine end")
}

range产生的迭代值为channel中发送的数据,它会一直迭代直到channel关闭且没有数据可接收时(buffer中有未读数据,则会继续读取,直到读完)跳出循环;

6 select

参见文章go语言之select

7 timer和ticker

timer和ticker是两个关于时间的channel;
timer是一个定时器,它提供一个channel,在等待时间过期后,将当前时间发送到此channel,下面例子会阻塞2秒,直到timer过期:

timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("Timer 1 expired")

如果只是单纯地等待,可以使用time.Sleep;
timer.Stop停止计时器,Stop并不close channel:

func main() {
    t := time.NewTimer(time.Second * 10)
    go func() {
        <-t.C
        fmt.Println("t expired")
    }()

    s := t.Stop()
    if s {
        fmt.Println("t stopped")
    }
    time.Sleep(time.Second * 60)
}

ticker是一个定时触发的计时器,它会以一个时间间隔向channel发送一个事件(当前时间),channel的接收者可以以固定的时间间隔读取事件,下面例子ticker每秒钟触发一次:

func main() {
    ticker := time.NewTicker(time.Second * 1)
    go func() {
        for t := range ticker.C {
            fmt.Println("Tick at ", t)
        }
        fmt.Println("ticker stop, exit for loop")
    }()
    time.Sleep(time.Second * 60)
    fmt.Println("ticker stop")
    ticker.Stop()
    time.Sleep(time.Hour * 1)
}

ticker也可以通过Stop方法停止,停止后接收者将不再会从channel中接收数据,Stop并不close channel。

8 同步

channel可以用于goroutine间同步

func worker(done chan bool) {
    time.Sleep(time.Second * 5)
    fmt.Println("send: done <- true")
    done <- true
}

func main() {
    done := make(chan bool, 1)
    go worker(done)
    <-done
    fmt.Println("done, exit")
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至yj.mapple@gmail.com

文章标题:go语言之channel

文章字数:1k

本文作者:melonshell

发布时间:2019-12-07, 15:34:04

最后更新:2020-07-26, 13:45:52

原始链接:http://melonshell.github.io/2019/12/07/go10_channel/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

相册