• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Golang极简入门教程(三):并发支持

    Golang 运行时(runtime)管理了一种轻量级线程,被叫做 goroutine。创建数十万级的 goroutine 是没有问题的。范例:

    复制代码 代码如下:

    package main
     
    import (
        "fmt"
        "time"
    )
     
    func say(s string) {
        for i := 0; i 5; i++ {
            time.Sleep(100 * time.Millisecond)
            fmt.Println(s)
        }
    }
     
    func main() {
        // 开启一个 goroutine 执行 say 函数
        go say("world")
        say("hello")
    }

    我们使用 channel 和 goroutine 通讯。channel 中是一种带有类型的通道,被用于接收和发送特定类型的值。操作符 - 被叫做 channel 操作符(这个操作符中箭头表明了值的流向):

    复制代码 代码如下:

    // 发送 v 到 channel ch
    ch - v
    // 接收 channel ch 中的值并赋值给 v
    v := -ch

    使用 channel 和 goroutine 通讯能够避免显式使用锁机制,通过 channel 发送和接收值时默认是阻塞的。

    通过 make 函数创建 channel:

    复制代码 代码如下:

    // int 指定 channel 收发值的类型为 int
    ch := make(chan int)

    一个完整的例子:

    复制代码 代码如下:

    package main
     
    import "fmt"
     
    // 计算数组 a 中所有元素值之和
    func sum(a []int, c chan int) {
        sum := 0
        for _, v := range a {
            sum += v
        }
        // 计算结果发送到 channel c
        c - sum
    }
     
    func main() {
        a := []int{7, 2, 8, -9, 4, 0}
     
        // 创建 channel c
        c := make(chan int)
     
        go sum(a[:len(a)/2], c)
        go sum(a[len(a)/2:], c)
     
        // 接收两个 goroutine 发送的计算结果
        x, y := -c, -c
     
        fmt.Println(x, y, x+y)
    }package main
     
    import "fmt"
     
    // 计算数组 a 中所有元素值之和
    func sum(a []int, c chan int) {
        sum := 0
        for _, v := range a {
            sum += v
        }
        // 计算结果发送到 channel c
        c - sum
    }
     
    func main() {
        a := []int{7, 2, 8, -9, 4, 0}
     
        // 创建 channel c
        c := make(chan int)
     
        go sum(a[:len(a)/2], c)
        go sum(a[len(a)/2:], c)
     
        // 接收两个 goroutine 发送的计算结果
        x, y := -c, -c
     
        fmt.Println(x, y, x+y)
    }

    channel 可以带有一个缓冲区(buffer)来缓存被传递的值,向 channel 中发送时只有缓冲区满的情况下会阻塞,接收 channel 中的值时只有在缓冲区空的情况下阻塞:

    复制代码 代码如下:

    package main
     
    import "fmt"
     
    func main() {
        // 创建 channel,缓冲区长度为 2
        c := make(chan int, 2)
        // 由于 channel 的缓冲区长度为 2
        // 因此发送不会阻塞
        c - 1
        c - 2
        fmt.Println(-c)
        fmt.Println(-c)
    }

    发送者可以调用 close 来关闭 channel,接收者可以检测到 channel 是否被关闭:

    复制代码 代码如下:

    // 这里的 ok 为 false 表示已经没有值可以接收了,并且 channel 被关闭了
    v, ok := -ch

    不要向已经关闭的 channel 发送值了(will cause a panic)。

    我们可以使用 for range 来接收 channel 中的值:

    复制代码 代码如下:

    package main
     
    import "fmt"
     
    func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i n; i++ {
            c - x
            x, y = y, x+y
        }
        // 必须要关闭 c
        close(c)
    }
     
    func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
        // 这里 for 和 range 组合使用
        // 不断的接收 c 中的值一直到它被关闭
        for i := range c {
            fmt.Println(i)
        }
    }

    通常来说,我们不需要主动的关闭 channel。但有时候接收者必须被告知已经没有值可以接收了,这时候主动关闭是必要的,例如终止 for range 循环。

    使用 select 语句可以让一个 goroutine 等待多个通讯操作。select 会阻塞直到某个 case 能够运行,如果同时存在多个可执行的,那么将随机选择一个:

    复制代码 代码如下:

    package main
     
    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)
    }

    select 中的 default 会在没有任何 case 可执行时执行(类似于 switch):

    复制代码 代码如下:

    package main
     
    import (
        "fmt"
        "time"
    )
     
    func main() {
        // 创建一个 tick channel
        // 在 100 毫秒后会向 tick channel 中发送当前时间
        tick := time.Tick(100 * time.Millisecond)
        // 创建一个 boom channel
        // 在 500 毫秒后会向 boom channel 中发送当前时间
        boom := time.After(500 * time.Millisecond)
        for {
            select {
            case -tick:
                fmt.Println("tick.")
            case -boom:
                fmt.Println("BOOM!")
                return
            default:
                fmt.Println("    .")
                time.Sleep(50 * time.Millisecond)
            }
        }
    }

    您可能感兴趣的文章:
    • golang分层测试之http接口测试入门教程
    • golang编程入门之http请求天气实例
    • Golang极简入门教程(四):编写第一个项目
    • Golang极简入门教程(二):方法和接口
    • Golang极简入门教程(一):基本概念
    • golang特有程序结构入门教程
    上一篇:Golang极简入门教程(二):方法和接口
    下一篇:Golang极简入门教程(四):编写第一个项目
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Golang极简入门教程(三):并发支持 Golang,极简,入门教程,三,