V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
haicoderibai
V2EX  ›  推广

Golang channe 你知多少

  •  
  •   haicoderibai · 2020-11-19 00:09:41 +08:00 · 929 次点击
    这是一个创建于 1469 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原文地址:嗨客网

    Golang channel 使用

    Golang channel 使用教程

    golangchannel 用于 goroutine 之间的通信,channel 是引用类型,需要使用 make 来创建 channel 。在 Golang 中,用通信来共享内存,而不要用共享内存来通信。

    channel 创建详解

    语法

    ch := make(chan Type, [buffer])
    

    参数

    ch: 创建的 channel 类型的变量 Type: channel 的类型 buffer: channel 缓冲区大小

    说明

    make 对于 channel 接收两个参数,第一个是通道的类型,第二个是个可选参数代表通道缓冲区的大小(省略代表无缓冲)。

    无缓冲 channel

    ch := make(chan int)
    

    带缓冲 channel

    ch := make(chan int, 2)
    

    channel 写入数据

    说明

    我们可以使用 <- 符号指向 channel 来将元素放入 channel 中,注意向通道中传值必须要求该通道还有容量(缓冲),而且通道不能关闭。

    对于无缓冲的或者缓冲已经满了的 channel 不可以轻易的传入值,必须要有 goroutine 同时在取元素才可以放入。

    示例

    向一个有缓冲,非满的 channel 传值

    c := make(chan int, 1) // 定义一个带有一个缓冲的通道
    c <- 1   // 向通道中传入一个 1,正常
    

    向一个有缓冲,满的 channel 传值

    c := make(chan int, 1) // 定义一个带有一个缓冲的通道
    c <- 1                 // 向通道中传入一个值,这个值传入后填满了该通道
    c <- 2                 // 再向通道中传入一个值,报错
    

    向一个无缓冲的 channel 传值

    c := make(chan int) // 定义一个无缓冲通道
    c <- 1              // 向无缓冲通道传值,报错
    

    其实上述两种错误均是由于通道满了而引起的(无缓冲的通道可以看成是缓冲为 0 的通道),解决方法很简单,只要保证有个 goroutine 同时在从该通道中取值即可。

    channel 读取数据

    说明

    我们可以使用 <- 符号指向变量来将 channel 中的元素放变量中,此时可以接收两个值一个数值一个状态,如下代码:

    v, ok := <-c   // c 是通道,v 是取到的值,ok 是状态,正常时是 true,从关闭的空通道取值是 false
    

    对于无缓冲的或者缓冲已经空了的 channel 不可以轻易的取出值,必须要同时在放元素才可以取出。

    示例

    ok 为 true 的例子

    c := make(chan int, 1)
    c <- 1
    a, ok := <-c
    fmt.Println(a, ok)  // 输出 1 true
    

    ok 为 false 的例子

    c := make(chan int, 1)
    close(c)
    a, ok := <-c
    fmt.Println(a, ok)  // 输出 0 false
    

    ok 的应用--循环取值

    for {
        v, ok := <- c
        if !ok {
            break
        }
        fmt.Println(v)
    }
    

    range 取值

    func main() {
        c := make(chan int, 10)
    
        for i:=0; i< 10; i++ {
            c <- i
        }
        close(c)
    
        for v := range c{
            fmt.Println(v)
        }
    }
    

    向一个有缓冲,非空的 channel 取值

    c := make(chan int, 1) // 定义一个带有一个缓冲的通道
    c <- 1                 // 向通道中传入一个 1,使通道非空
    i := <-c               // 从通道中取出一个值赋给变量 i
    // 如果只是想取出值而不想对该值做任何其他操作,可以这么写   <-c   左边省略接收者
    

    向一个有缓冲,空的 channel 取值

    c := make(chan int, 1) // 定义一个带有一个缓冲的通道
    <-c                    // 向空通道中取出一个值,报错
    

    向一个无缓冲的 channel 取值

    c := make(chan int) // 定义一个无缓冲通道
    <-c                 // 向无缓冲通道取值,报错
    

    其实上述两种错误均是由于通道空了而引起的(无缓冲的通道可以看成是缓冲为 0 的通道),解决方法很简单,只要保证同时在从该通道中存值即可。

    关闭 channel 详解

    说明

    对于一个通道我们可以使用 close 内置函数来进行关闭,关闭后的通道具有以下特点:

    • 向一个已经关闭的通道发送值是不允许的,会报错
    • 从一个已经关闭但是里面还有值的通道取值是允许的,可以正常获取到值
    • 从一个已经关闭但是为空的通道取值是允许的,会获取通道类型元素的零值
    • 不可以再次关闭一个已经关闭的通道,会报错
    • 已经关闭的通道无法再次打开

    示例

    向一个已经关闭的通道发送值

    c := make(chan int, 1)
    close(c)
    c <- 1   // 报错
    

    从一个已经关闭但是里面还有值的通道取值

    c := make(chan int, 1)
    c <- 1
    close(c)
    a := <-c
    fmt.Println(a)  // 输出 1
    

    从一个已经关闭但是为空的通道取值

    c := make(chan int, 1)
    close(c)
    a := <-c
    fmt.Println(a)  // 输出 0 
    

    关闭一个已经关闭的通道

    c := make(chan int, 1)
    close(c)
    close(c) // 报错: panic: close of closed channel
    

    单向通道

    说明

    在函数中使用通道时我们可以限制其为只读通道或者只写通道。

    只读通道

    func doWork(i <-chan int) {
        <-i      // 只能取值
        //i <- 1 // 存值操作将不被允许
    }
    

    只写通道

    func doWork(i chan <- int) {
        i <- 1      // 只能存值
        //<-i       // 取值操作将不被允许
    }
    

    案例

    channel 通信

    使用 channel 实现通信

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    var wg sync.WaitGroup
    
    func main() {
        fmt.Println("嗨客网(www.haicoder.net)")
    
        c := make(chan int, 1)
    
        c <- 1  // 因为我们定义的通道带有一个缓冲,
        // 所以在发布任务之前允许存在最多一个任务投递,也可以写在发布任务之后
    
        // for 循环用于发布任务
        for i := 0; i < 2; i++ {
            wg.Add(1)
            go doWork(c)    // 如果通道现在是空的 goroutine 会等待,
            // 如果检测到现在是空的且没有任务投递,就会报错
        }
    
        c <- 2  // 多余的任务投递必须在任务发布之后
    
        wg.Wait()
    }
    
    func doWork(i chan int) {
        defer wg.Done()
        a := <-i
        fmt.Printf("%d 号开始工作了\n", a)
        time.Sleep(time.Second * 2) // 模拟耗时
        fmt.Printf("%d 号结束工作了,输出:%d\n", a, a)
    }
    

    此时,我们运行程序,控制台输出如下:

    www.haicoder.net

    我们使用了 channel 实现了,收发数据。

    channel 通信

    使用 channel 实现通信

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    var wg sync.WaitGroup
    
    func main() {
        fmt.Println("嗨客网(www.haicoder.net)")
    
        c := make(chan int, 1)
        wg.Add(1)
        go doWork(c)
        <-c         // 取值必须在放值之后
        wg.Wait()
    }
    
    func doWork(i chan int) {
        defer wg.Done()
        fmt.Printf("goroutine 开始工作了\n", )
        time.Sleep(time.Second * 2) // 模拟耗时
        i <- 1
        fmt.Printf("goroutine 结束工作了,放入:%d\n", 1)
    }
    

    此时,我们运行程序,控制台输出如下:

    我们使用了 channel 实现了,收发数据。

    Golang channel 使用总结

    在 golang 中 channel 用于 goroutine 之间的通信,channel 是引用类型,需要使用 make 来创建 channel 。在 Golang 中,用通信来共享内存,而不要用共享内存来通信。

    原文地址:嗨客网

    更多文章,可以关注下方公众号:

    嗨客网

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3536 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:55 · PVG 12:55 · LAX 20:55 · JFK 23:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.