Go:缓存和非缓冲 Channel

393 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

在 Golang 中,当谈论并发时,肯定少不了要说下并发通信 Channel。Channel 可以在 goroutine 之间进行通信。 我们可以从一个 goroutine 向另一个 goroutine 发送和接收消息。

非缓冲通道

非缓冲通道是指最初没有存储消息能力的通道。然后需要我们向非缓冲通道填充消息,以使 goroutine 进程不被通道阻塞。 例如:

func main() {

    ch := make(chan int)

    // 从 ch 中读取数据
    go func() {
        time.Sleep(time.Second * 5)
        for i := range ch {
            fmt.Println(i)
            time.Sleep(time.Second)
        }
    }()

    for i := 0; i < 9; i++ {
        ch <- i // 向 ch 中写入数据
        fmt.Println("Filled")
    }

}

在上面的例子中,我们的 main 函数中有一个循环不断的向 ch 填充数据, 但是它被阻塞了。 但是还有一个协程在异步的执行从 ch 中读取数据并打印出来。异步函数一定要在主函数先执行,不然写入 ch 就会被堵塞。

上面示例的输出结果:

2022-10-15-23-22-12-image.png

缓冲通道

与无缓冲通道不同,缓冲通道具有在其中存储消息的能力。 缓冲通道可以填充到其定义的容量,而不仅仅是一条消息。 例如:

func main() {
    ch := make(chan int, 3)
    // 从 ch 中读取数据
    go func() {
        time.Sleep(time.Second)
        fmt.Println("ch 填充满了开始读取了")
        for i := range ch {
            fmt.Println(i)
            time.Sleep(time.Second)
        }
    }()

    for i := 0; i < 9; i++ {
        ch <- i // 向 ch 中写入数据
        fmt.Println("Filled")
    }
}

执行结果如下图:

2022-10-15-23-21-21-image.png

如您所见,可以先填充 ch 直到它满容量,然后另一个 goroutine 可以一个一个地访问它。

不同点

  • 非缓冲 Channel 最初没有容量,但 缓存 Channel 有容量。
  • 非缓冲的 Channel 将在 goroutine 为空并等待填充时阻塞。 但是缓冲通道也会在 goroutine 为空并等待填充时阻塞,或者当它处于满容量并且有一条语句想要填充通道时。

总结

设置容量很简单,只需要给他分配长度即可。

noBuffered :=make(chan int) // 非缓冲
buffered :=make(chan int, 30) //缓冲
  • 缓存 channel 和 非缓冲 channel 都会阻塞 goroutine,直到它被填满,在这种情况下会阻塞一秒钟。

  • 写入 channel 写满后,就会堵塞 channel ,只有 channel 被读取释放出来才不会造成堵塞。所以需要其他协程去读取。