Go语言Chan应用

幸运草 2020年4月19日22:13:13函数代码评论阅读模式

Go语言Chan应用

Channel 是 CSP 模式的具体实现,用于多个 goroutine 通讯。其内部实现了同步,确保并发安全。
Channel 是先进先出,线程安全的,多个goroutine同时访问,不需要加锁。

chan 阻塞

我们定义的管道 intChan 容量是5,开启 goroutine 写入10条数据,在写满5条数据时会阻塞,而 read() 每秒会从 intChan 中读取一条,然后write() 再会写入一条数据。

package main

import (
    "fmt"
    "time"
)

func write(ch chan int) {
    for i := 0; i < 10; i++ {
        ch <- i
        fmt.Println("write data:", i)
    }
}

func read(ch chan int) {
    for {
        i := <-ch
        fmt.Println("read data:", i)
        time.Sleep(time.Second)
    }
}

func main() {
    intChan := make(chan int, 5)
    go write(intChan)
    go read(intChan)

    time.Sleep(10 * time.Second)
}

同步模式

默认为同步模式,需要发送和接收配对。否则会被阻塞,直到另一方准备好后被唤醒。

package main

import "fmt"

func main() {
    data := make(chan string) // 数据交换队列
    exit := make(chan bool)   // 退出通知

    go func() {
        for d := range data { // 从队列迭代接收数据,直到 close 。
            fmt.Println(d)
        }

        fmt.Println("received over.")
        exit <- true // 发出退出通知。
    }()

    data <- "oldboy" // 发送数据。
    data <- "Linux"
    data <- "Golang"
    data <- "Python"
    close(data) // 关闭队列。
    fmt.Println("send over.")
    <-exit // 等待退出通知。
}

异步模式

异步方式通过判断缓冲区来决定是否阻塞。如果缓冲区已满,发送被阻塞;缓冲区为空,接收被阻塞。

通常情况下,异步 channel 可减少排队阻塞,具备更高的效率。但应该考虑使用指针规避大对象拷贝,将多个元素打包,减小缓冲区大小。

package main

import (
    "fmt"
)

func main() {
    data := make(chan string, 3) // 缓冲区可以存储 3 个元素
    exit := make(chan bool)

    data <- "oldboy" // 在缓冲区未满前,不会阻塞。
    data <- "Linux"
    data <- "Golang"

    go func() {
        for d := range data { // 在缓冲区未空前,不会阻塞。
            fmt.Println(d)
        }

        exit <- true
    }()

    data <- "Java" // 如果缓冲区已满,阻塞。
    data <- "DBA"
    close(data)

    <-exit
}

chan 选择

如果需要同时处理多个 channel,可使用 select 语句。它随机选择一个可用 channel 做收发操作,或执行 default case。

用 select 实现超时控制:

package main

import (
    "fmt"
    "time"
)

func main() {
    exit := make(chan bool)
    intChan := make(chan int, 2)
    strChan := make(chan string, 2)

    go func() {
        select {
        case vi := <-intChan:
            fmt.Println(vi)
        case vs := <-strChan:
            fmt.Println(vs)
        case <-time.After(time.Second * 3):
            fmt.Println("timeout.")
        }

        exit <- true
    }()

    // intChan <- 100 // 注释掉,引发 timeout。
    // strChan <- "oldboy"

    <-exit
}

在循环中使用 select default case 需要小心,避免形成洪水。

简单工厂模式

用简单工厂模式打包并发任务和 channel。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func NewTest() chan int {
    c := make(chan int)
    rand.Seed(time.Now().UnixNano())
    go func() {
        time.Sleep(time.Second)
        c <- rand.Int()
    }()
    return c
}
func main() {
    t := NewTest()
    fmt.Println(<-t) // 等待 goroutine 结束返回。
}

特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
幸运草
Go语言中的常量 函数代码

Go语言中的常量

1 概述 常量,一经定义不可更改的量。功能角度看,当出现不需要被更改的数据时,应该使用常量进行存储,例如圆周率。从语法的角度看,使用常量可以保证数据,在整个运行期间内,不会被更改。例如当前处理器的架构...
Go语言的接口 函数代码

Go语言的接口

Go语言-接口 在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type、类型名称、关键字interface以及由花括号包裹的若干方法声明...
Go语言支持的正则语法 函数代码

Go语言支持的正则语法

1 字符 语法 说明 . 任意字符,在单行模式(s标志)下,也可以匹配换行 字符类 否定字符类 d Perl 字符类 D 否定 Perl 字符类 ASCII 字符类 否定 ASCII 字符类 pN U...
Go语言的包管理 函数代码

Go语言的包管理

1 概述 Go 语言的源码复用建立在包(package)基础之上。包通过 package, import, GOPATH 操作完成。 2 main包 Go 语言的入口 main() 函数所在的包(pa...

发表评论