MST
星途 面试题库

面试题:Go语言中通道(channel)的缓冲与非缓冲有何区别

请详细阐述Go语言中缓冲通道与非缓冲通道在定义、特性以及适用场景方面的区别,并举例说明如何创建这两种通道。
16.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

定义区别

  • 非缓冲通道:也叫无缓冲通道,在创建时不指定缓冲区大小。定义格式为 make(chan Type),例如 ch := make(chan int)。这种通道在发送和接收操作时,发送方和接收方必须同时准备好,它们之间是同步的。
  • 缓冲通道:创建时指定了缓冲区大小。定义格式为 make(chan Type, capacity),例如 ch := make(chan int, 5)。缓冲区用于暂存数据,使得发送操作在缓冲区未满时不需要立即有接收方准备好。

特性区别

  • 非缓冲通道
    • 数据传递是同步的,发送操作和接收操作会阻塞,直到双方都准备好。
    • 保证收发数据的顺序性,先发送的数据先被接收。
  • 缓冲通道
    • 发送操作在缓冲区未满时不会阻塞,接收操作在缓冲区非空时不会阻塞。
    • 当缓冲区满时发送操作会阻塞,缓冲区空时接收操作会阻塞。
    • 数据顺序性依赖于发送和接收操作的顺序,缓冲区只是暂存数据。

适用场景区别

  • 非缓冲通道
    • 适用于需要确保数据同步传递的场景,比如在多个 goroutine 之间进行精确的同步操作,例如信号传递。
    • 例如实现一个简单的信号同步,主 goroutine 等待子 goroutine 完成任务后再继续执行:
package main

import (
    "fmt"
)

func main() {
    done := make(chan struct{})
    go func() {
        // 模拟一些工作
        fmt.Println("子 goroutine 工作中")
        done <- struct{}{}
    }()
    <-done
    fmt.Println("主 goroutine 继续执行")
}
  • 缓冲通道
    • 适用于解耦发送方和接收方的场景,例如生产者 - 消费者模型,生产者可以持续向缓冲区发送数据,消费者从缓冲区消费数据,只要缓冲区有空间或有数据,它们就可以异步工作。
    • 如下是简单的生产者 - 消费者示例:
package main

import (
    "fmt"
)

func producer(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Printf("生产者发送: %d\n", i)
    }
    close(ch)
}

func consumer(ch chan int) {
    for num := range ch {
        fmt.Printf("消费者接收: %d\n", num)
    }
}

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

创建示例

  • 非缓冲通道创建
package main

import "fmt"

func main() {
    // 创建非缓冲通道
    unbufferedChan := make(chan int)
    go func() {
        unbufferedChan <- 42
    }()
    value := <-unbufferedChan
    fmt.Println("接收到的值:", value)
}
  • 缓冲通道创建
package main

import "fmt"

func main() {
    // 创建缓冲通道,缓冲区大小为 2
    bufferedChan := make(chan int, 2)
    bufferedChan <- 10
    bufferedChan <- 20
    fmt.Println("从缓冲通道接收:", <-bufferedChan)
    fmt.Println("从缓冲通道接收:", <-bufferedChan)
}