golang-channel

发布时间 2023-09-28 14:37:32作者: 意犹未尽

什么是 channel 管道

channel 是 goroutine 与 goroutine 之间通信的重要桥梁

channel 是一个通道,用于端到端的数据传输,这有点像我们平常使用的消息队列,只不过 channel 的发送方和接受方是 goroutine 对象,属于内存级别的通信。

这里涉及到了 goroutine 概念,goroutine 是轻量级的协程,有属于自己的栈空间。 我们可以把它理解为线程,只不过 goroutine 的性能开销很小,并且在用户态上实现了属于自己的调度模型。

传统的线程通信有很多方式,像内存共享、信号量等。其中内存共享实现较为简单,只需要对变量进行并发控制,加锁即可。但这种在后续业务逐渐复杂时,将很难维护,耦合性也比较强。

后来提出了 CSP 模型,即在通信双方抽象出中间层,数据的流转由中间层来控制,通信双方只负责数据的发送和接收,从而实现了数据的共享,这就是所谓的通过通信来共享内存。 channel 就是按这个模型来实现的。

channel 在多并发操作里是属于协程安全的,并且遵循了 FIFO 特性。即先执行读取的 goroutine 会先获取到数据,先发送数据的 goroutine 会先输入数据。

另外,channel 的使用将会引起 Go runtime 的调度调用,会有阻塞和唤起 goroutine 的情况产生。

channel的2种类型

无缓冲

ch := make(chan int)

上面是创建了无缓冲的 channel,一旦有 goroutine 往 channel 发送数据,那么当前的 goroutine 会被阻塞住,直到有其他的 goroutine 消费了 channel 里的数据,才能继续运行。

有缓冲

ch := make(chan int, 2)

第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量,则当前的 goroutine 就不会被阻塞住。

nil的channel

我们也可以声明一个 nil 的 channel,只是创建这样的 channel 没有意义,读、写 channel 都将会被阻塞住。一般 nil channel 用在 select 上,让 select 不再从这个 channel 里读取数据,

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        for i := 0; i < 3; i++ {
            time.Sleep(1 * time.Second)
            ch1 <- 2
        }
        fmt.Println("ch1  set null")
        //模拟某些业务场景写入nil
        ch1 = nil
    }()

    go func() {
        for {
            time.Sleep(1 * time.Second)
            ch2 <- 2
        }
    }()

    for {
        select {
        case <-ch1: // 当 ch1 被设置为 nil 后,将不会到达此分支了。
            fmt.Println("ch1 process")
        case <-ch2:
            fmt.Println("ch2 process")
        }
    }
}

输出

ch1 process
ch2 process
ch1 process
ch2 process
ch1  set null
ch1 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
ch2 process
Exiting.
View Code

如何限制channel只读只写

只读

只写