美文网首页
13. Go极简教程 channel信道

13. Go极简教程 channel信道

作者: 超级大柱子 | 来源:发表于2018-06-03 15:51 被阅读167次

    channel 信道的创建

    ci := make(chan int)            // 整数类型的无缓冲信道
    cj := make(chan int, 0)         // 整数类型的无缓冲信道
    cs := make(chan *os.File, 100)  // 指向文件指针的带缓冲信道
    

    channel 是带缓冲和阻塞的信道

    默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。

    ch := make(chan int)
    ch <- v    // 将 v 送入 channel ch。
    v := <-ch  // 从 ch 接收,并且赋值给 v。
    (“箭头”就是数据流的方向。)
    

    goroutline配合channel, 以实现多线程的同步操作

    向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。

    package main
    
    import (
        "log"
        "time"
    )
    
    // 声明一个函数,
    func add(a int, c chan int) {
        log.Println("准备往管道添加:", a)
        c <- a
    }
    
    func main() {
        a := []int{1, 2, 3, 4, 5, 6}
    
        // 声明了一个管道, 管道长度为0
        c := make(chan int)
        // 做两个循环, 开启多个协程
        for _, v := range a[:len(a)/2] {
            go add(v, c)
        }
        for _, v := range a[len(a)/2:] {
            go add(v, c)
        }
    
        // 上面的chan是阻塞的, 每当这里取出一个值, 上面的协程才会继续再执行一次
        var x, y, n int
        for i := 0; i < len(a)/2; i++ {
            n = <-c
            log.Println("从管道取出:", n, "并加入到x中")
            x += n
    
            n = <-c
            log.Println("从管道取出:", n, "并加入到y中")
            y += n
        }
    
        time.Sleep(500 * time.Microsecond)
        log.Printf("x:%d, y:%d, sum:%d", x, y, x+y)
    }
    
    

    打印的内容(执行的顺序每次不一, 但是sum的总和是一致的):

    2018/06/03 15:50:14 准备往管道添加: 6
    2018/06/03 15:50:14 从管道取出: 6 并加入到x中
    2018/06/03 15:50:14 准备往管道添加: 2
    2018/06/03 15:50:14 从管道取出: 2 并加入到y中
    2018/06/03 15:50:14 准备往管道添加: 5
    2018/06/03 15:50:14 从管道取出: 5 并加入到x中
    2018/06/03 15:50:14 准备往管道添加: 1
    2018/06/03 15:50:14 准备往管道添加: 3
    2018/06/03 15:50:14 从管道取出: 1 并加入到y中
    2018/06/03 15:50:14 从管道取出: 3 并加入到x中
    2018/06/03 15:50:14 准备往管道添加: 4
    2018/06/03 15:50:14 从管道取出: 4 并加入到y中
    2018/06/03 15:50:14 x:14, y:7, sum:21
    

    range 和 close

    发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过

    v, ok := <-ch
    

    之后 ok 会被设置为 false

    循环 for i := range c 会不断从 channel 接收值,直到它被关闭。

    注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range

    package main
    
    import "log"
    
    func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ {
            c <- x
            x, y = y, x+y
        }
        // close 关闭一个 channel
        // close 只能由发送者(协程的函数)执行
        close(c)
    }
    
    func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
    
        //还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`
        for v := range c {
            log.Println(v)
        }
    }
    
    

    参考资料:
    http://go-tour-zh.appspot.com/

    Go极简教程 继续阅读( 目录)

    相关文章

      网友评论

          本文标题:13. Go极简教程 channel信道

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