MST

星途 面试题库

面试题:Go语言中Channel的缓冲与非缓冲有何区别

在Go语言的协程通信中,Channel分为缓冲和非缓冲两种类型,请阐述这两种类型Channel的区别,并且分别举例说明在什么场景下会选择使用缓冲Channel和非缓冲Channel。
20.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

区别

  1. 非缓冲Channel
    • 也叫同步Channel,发送和接收操作会阻塞,直到对应的接收和发送操作准备好。也就是说,当一个协程向非缓冲Channel发送数据时,它会一直阻塞,直到另一个协程从该Channel接收数据;反之,当一个协程尝试从非缓冲Channel接收数据时,它会阻塞,直到有其他协程向该Channel发送数据。
    • 非缓冲Channel保证数据的同步传递,数据从发送端直接传递到接收端,没有中间的缓存空间。
  2. 缓冲Channel
    • 具有一定的缓存容量,可以在没有接收者的情况下,先缓冲一定数量的数据。只有当Channel的缓存被填满时,再进行发送操作才会阻塞;同样,只有当Channel为空时,接收操作才会阻塞。
    • 缓冲Channel允许在一定程度上解耦发送者和接收者的操作,提高了并发操作的灵活性。

场景举例

  1. 缓冲Channel适用场景
    • 生产者 - 消费者模型: 例如,有一个生产者协程不断生成数据,消费者协程处理数据。如果生产者生成数据的速度比消费者处理数据的速度快一些,并且允许一定程度的堆积,可以使用缓冲Channel。
package main

import (
    "fmt"
)

func producer(ch chan int) {
    for i := 0; i < 10; i++ {
        ch <- i
        fmt.Printf("Produced: %d\n", i)
    }
    close(ch)
}

func consumer(ch chan int) {
    for num := range ch {
        fmt.Printf("Consumed: %d\n", num)
    }
}

func main() {
    ch := make(chan int, 5)
    go producer(ch)
    go consumer(ch)
    select {}
}

在这个例子中,ch是一个缓冲容量为5的Channel。生产者可以先向Channel中发送最多5个数据,而不会阻塞,消费者在合适的时候从Channel中取出数据。这在一定程度上缓解了生产者和消费者速度不匹配的问题。 2. 非缓冲Channel适用场景

  • 同步操作: 假设需要两个协程严格按照顺序执行某些操作,例如一个协程进行初始化操作,另一个协程在初始化完成后再开始执行任务。
package main

import (
    "fmt"
)

func initializer(ch chan struct{}) {
    fmt.Println("Initializing...")
    // 模拟一些初始化工作
    ch <- struct{}{}
    fmt.Println("Initialization completed")
}

func worker(ch chan struct{}) {
    <-ch
    fmt.Println("Starting work...")
}

func main() {
    ch := make(chan struct{})
    go initializer(ch)
    go worker(ch)
    select {}
}

这里使用非缓冲Channel ch来确保worker协程在initializer协程完成初始化(通过向ch发送数据)后才开始工作,实现了严格的同步。