MST

星途 面试题库

面试题:Go中并发模型里的通道(channel)如何实现数据同步与通信

在Go的并发编程中,通道是重要的组成部分。请阐述通道的基本原理,包括其类型声明、发送和接收操作。同时,举例说明如何利用通道在两个并发的goroutine之间安全地传递数据,并实现简单的同步,例如一个goroutine生成数据,另一个goroutine消费数据。
27.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

通道基本原理

  1. 类型声明: 在Go语言中,通道是一种类型,用于在不同的goroutine之间进行通信。其声明语法如下:
    var ch chan int // 声明一个可以传递int类型数据的通道
    ch := make(chan int) // 使用make函数创建一个可以传递int类型数据的通道
    
    通道类型可以是带缓冲的或无缓冲的。带缓冲通道在创建时指定缓冲大小,例如 ch := make(chan int, 10),表示该通道可以缓存10个int类型的数据。无缓冲通道则没有缓存,发送操作会阻塞直到有接收操作准备好接收数据。
  2. 发送操作: 使用 <- 操作符向通道发送数据。例如:
    ch <- 10 // 将10发送到通道ch
    
    如果通道是无缓冲的,当执行发送操作时,如果没有接收者准备好接收数据,发送操作会阻塞当前goroutine,直到有接收者接收数据。如果通道是有缓冲的,当通道未满时,发送操作不会阻塞;当通道已满时,发送操作会阻塞,直到有接收者接收数据,腾出空间。
  3. 接收操作: 同样使用 <- 操作符从通道接收数据。有两种接收方式:
    data := <-ch // 从通道ch接收数据,并赋值给data
    data, ok := <-ch // 从通道ch接收数据,并通过ok判断通道是否关闭。如果通道已关闭且无数据可接收,ok为false
    
    当从无缓冲通道接收数据时,如果没有数据可接收,接收操作会阻塞当前goroutine,直到有数据发送进来。从有缓冲通道接收数据时,如果通道中有数据,接收操作不会阻塞;如果通道为空,接收操作会阻塞,直到有数据发送进来。

示例代码

package main

import (
    "fmt"
)

func producer(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Printf("Produced: %d\n", i)
    }
    close(ch) // 生产完成后关闭通道
}

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

func main() {
    ch := make(chan int)
    go producer(ch)
    go consumer(ch)

    // 防止主函数退出
    select {}
}

在上述代码中:

  1. producer 函数是一个goroutine,它生成数据并通过通道 ch 发送出去,在生产完5个数据后关闭通道。
  2. consumer 函数也是一个goroutine,它使用 for... range 循环从通道 ch 接收数据,当通道关闭且无数据可接收时,循环结束。
  3. main 函数中,创建了通道 ch,并启动了 producerconsumer 两个goroutine,通过通道实现了两个goroutine之间的数据安全传递和简单同步。select {} 语句用于防止主函数退出,确保两个goroutine有足够时间完成任务。