Channel

作者: 集韵增广 | 来源:发表于2021-11-01 18:03 被阅读0次

    Channel是在goroutine之间同步的方式

    定义

    ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
    它包括三种类型的定义。可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据

    例子:
    申明:ch chan int
    创建对象:ch := make(chan int)  或者 ch := make(chan int, 100) // 100就是容量,即这个管道里可以存100个int

    收发

    SendStmt = Channel "<-" Expression .
    Channel = Expression .

    例子:
    c := make(chan int)
    defer close(c)
    发给channel:c <- 1+2
    收消息:i := <-c
    注意:
    没有缓存的channel如果接收端没准备好发送程序不执行;如果有缓存且缓存满了也不执行;
    如果channel被close了,发送数据的程序会出错(run-time panic)
    如果channel被close了,接受数据的程序会直接返回,接收完已发送的数据后会返回元素类型的零值(zero value)。
    比如:
    x, ok := <-ch
    x, ok = <-ch
    第一次收到数据,第二次数据空了,如果ok是false,表明接收的x值是产生的零值,这个channel被关闭了或者空

    阻塞

    import "fmt"
    func sum(s []int, c chan int) {
      sum := 0
      for _, v := range s {
        sum += v
      }
      c <- sum // send sum to c
    }
    func main() {
      s := []int{7, 2, 8, -9, 4, 0}
      c := make(chan int)
      go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c)
      x, y := <-c, <-c // receive from c
      fmt.Println(x, y, x+y)
    }

    迭代

    range c产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭。上面的例子中如果把close(c)注释掉,程序会一直阻塞在for …… range那一行。
    func main() {
      go func() { time.Sleep(1 * time.Hour) }()
      c := make(chan int) go func() { for i := 0; i < 10; i = i + 1 { c <- i } close(c) }()
      for i := range c { fmt.Println(i) }
      fmt.Println("Finished")
    }

    SELECT

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

    import "fmt"
    func fibonacci(c, quit chan int) {
        x, y := 0, 1
        for {
            select {
                 case c <- x:
                       x, y = y, x+y
                 case <-quit:
                       fmt.Println("quit")
                       return
           }
       }
    }
    func main() {
        c := make(chan int)
        quit := make(chan int)
        go func() {
              for i := 0; i < 10; i++ { fmt.Println(<-c) }
              quit <- 0
        }()
        fibonacci(c, quit)
    }
    如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。
    如果没有case需要处理,则会选择default去处理,如果default case存在的情况下。如果没有default case,则select语句会阻塞,直到某个case需要处理。
    需要注意的是,nil channel上的操作会一直被阻塞,如果没有default case,只有nil channel的select会一直被阻塞。select语句和switch语句一样,它不是循环,它只会选择一个case来处理,如果想一直处理channel,可以在外面加一个无限的for循环

    超时

    上面例子中如果没有消息,则一直会阻塞在select,所以需要加个timeout

    go func() { time.Sleep(time.Second * 2) c <- "result 1" }()

    关于时间的Channel

    Timer:
    timer1 := time.NewTimer(time.Second * 2)
    <-timer1.C
    fmt.Println("Timer 1 expired")
    stop1 := timer1.Stop()
    if stop1 { fmt.Println("Timer1 stop!") }

    Ticker:
    ticker := time.NewTicker(time.Millisecond * 500)
    go func() { for t := range ticker.C { fmt.Println("Tick at", t) }}()
    stop1 := ticker1.Stop()
    if stop1 { fmt.Println("Ticker1 stop!") }

    CLOSE

    close后:
    如果继续往队列里发送数据,会导致:panic: send on closed channel
    如果继续从队列里读数据,会可以不停读取,value, ok <- c,value会一直为0,ok会是false

    相关文章

      网友评论

          本文标题:Channel

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