golang 信道的讲解与应用--channel五

其实关于 golang 信道网上资料很多很全面了,个人感觉也没什么需要特别注意的坑。但是为了 golang 系列的的完整性,我还是开了这一篇博客,为大家提供一些 golang 使用的例子,多一些参考文档。

在上一篇中我们提到 golang 通过 go 程提供了非常优秀的高并发能力,那么 go 程之间的通信就是通过 channel 来进行的。和 go 程一样,golang 在语法上就支持 channel,golang 为 chennle 专门实现了一种变量类型 chan。可以和创建普通变量一样创建 channel。另外 chan 类型的变量的一个重要特性:线程安全。其他类型变量在高并发场景,如果要直接操作这个变量需要先加锁,chan 则完全避免了这个烦恼。

我们来直接看例子,这个例子时基于上一章的第一个例子做了些许的改动:

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    go func(ch chan int) {
        fmt.Println("Hello, I am a goroutine.")
        ch <- 0
    }(ch)
    <-ch
    fmt.Println("Hello, I am the main goroutine.")
}

输出:

Hello, I am a goroutine.
Hello, I am the main goroutine.

这里我们就没有用到 time.Sleep,在上一章节我们提到如果没有 Sleep 主 go 程就会先退出,还没等子 go 程执行完毕整个程序就结束了。这里我们用来一种比 Sleep 更优雅的方式 chan 使主 go 程阻塞,等等子 go 程想 chan 写入数据,主 go 程收到数据才继续往下执行。这里借助了 channel 另一个比较重要的特性:当 channel 中没有数据,读时就会阻塞;当 channel 满了之后向 channel 写数据也就会阻塞。通过这种方式就能及时知道子 go 程退出了,避免了长时间的等待。

另外需要特别注意的是阻塞发生的时机,下面例子需要实际运行看结果,大家可以自己运行一下对比结果:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    go func(ch chan int) {
        for {
            time.Sleep(5 * time.Second)
            <-ch
            fmt.Println("子 go 程从 ch 中读到数据")
            // 把 time.Sleep(5 * time.Second) 挪到这儿试试
        }
    }(ch)
    fmt.Println("准备向 channel 写入数据")
    ch <- 0
    fmt.Println("写入成功")
}

另外在有些情况我们并不希望因为 channel 数据接收端(消费者)的 go 程处理数据过慢而阻塞 channel 数据发送端(生产者)的 go 程,这时候我们可以使用带缓存的 channel。可以使用下面方法创建带缓存的 channel。

ch := make(chan int, 10)

这将会创建一个 10 个缓存空间的 channel。

单方向的 channel

channel 还可以设置成只读或者只写

创建只写 channel:

var ch chan<- int

或者:

ch := make(chan<- int, 10)

创建只读 channel

var ch <-chan int

或者:

ch := make(<-chan int, 10)

特别注意:如果直接使用单方向的 channel 那么程序必然或阻塞。正确的使用方式是先创建一个正常的 channel,然后在隐式的转为只读和只写,生产者使用只写 channel,消费者使用只读 channel,如下:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 10)
    go producer(ch)
    go consumer(ch)
    time.Sleep(1 * time.Millisecond)
}

func producer(ch chan<- int) {
    a := 10
    fmt.Printf("生产者发送数据:%d\n", a)
    ch <- a
}

func consumer(ch <-chan int) {
    a := <-ch
    fmt.Printf("消费者接收到数据:%d\n", a)
}

输出:

生产者发送数据:10
消费者接收到数据:10

在前面第二章节讲过 channel 类型的数据变量事一个引用,这里通过函数传参的方式进行隐式的转换只是作用在了引用本身上,实际转换前和转换后变量对应的底层数据是一样的。

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...