MST
星途 面试题库

面试题:Go语言中函数间使用channel通信的常见模式有哪些

请阐述在Go语言里,函数间利用channel进行通信时,至少三种常见的通信模式,并举例说明。
48.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 单向数据发送模式

在这种模式下,一个goroutine通过channel向另一个goroutine发送数据。

package main

import (
    "fmt"
)

func sender(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

func main() {
    ch := make(chan int)
    go sender(ch)
    for val := range ch {
        fmt.Println(val)
    }
}

在上述代码中,sender函数向ch这个单向发送的channel发送数据,主goroutine从该channel接收数据并打印。

2. 单向数据接收模式

与单向发送模式相反,一个goroutine从channel接收数据,另一个goroutine向该channel发送数据。

package main

import (
    "fmt"
)

func receiver(ch <-chan int) {
    for val := range ch {
        fmt.Println(val)
    }
}

func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
        close(ch)
    }()
    receiver(ch)
}

这里receiver函数从单向接收的ch channel接收数据,匿名goroutine向该channel发送数据。

3. 双向数据通信模式

两个goroutine之间可以通过同一个channel进行数据的发送和接收。

package main

import (
    "fmt"
)

func talk(ch chan int) {
    for i := 0; i < 3; i++ {
        ch <- i
        val := <-ch
        fmt.Printf("Received %d back\n", val)
    }
    close(ch)
}

func main() {
    ch := make(chan int)
    go talk(ch)
    for val := range ch {
        newVal := val * 2
        ch <- newVal
    }
}

talk函数中,先向ch发送数据,然后又从ch接收数据。主goroutine从ch接收数据并处理后再发回ch

4. 扇入(Fan - In)模式

多个goroutine向同一个channel发送数据,由一个goroutine从该channel接收数据。

package main

import (
    "fmt"
)

func worker(id int, ch chan<- int) {
    for i := 0; i < 3; i++ {
        ch <- id*10 + i
    }
    close(ch)
}

func fanIn(inputs []<-chan int, output chan<- int) {
    var wg sync.WaitGroup
    for _, in := range inputs {
        wg.Add(1)
        go func(c <-chan int) {
            defer wg.Done()
            for val := range c {
                output <- val
            }
        }(in)
    }
    go func() {
        wg.Wait()
        close(output)
    }()
}

func main() {
    var inputs []chan int
    for i := 0; i < 2; i++ {
        ch := make(chan int)
        inputs = append(inputs, ch)
        go worker(i, ch)
    }
    output := make(chan int)
    fanIn(inputs, output)
    for val := range output {
        fmt.Println(val)
    }
}

这里有多个worker goroutine向不同的channel发送数据,fanIn函数将这些channel的数据合并到一个输出channel。

5. 扇出(Fan - Out)模式

一个goroutine向多个channel发送数据,多个goroutine从这些不同的channel接收数据。

package main

import (
    "fmt"
    "sync"
)

func distributor(ch <-chan int, outputs []chan<- int) {
    var wg sync.WaitGroup
    for _, out := range outputs {
        wg.Add(1)
        go func(c chan<- int) {
            defer wg.Done()
            for val := range ch {
                c <- val
            }
            close(c)
        }(out)
    }
    go func() {
        wg.Wait()
        for _, out := range outputs {
            close(out)
        }
    }()
    for val := range ch {
        for _, out := range outputs {
            out <- val
        }
    }
    close(ch)
}

func main() {
    input := make(chan int)
    var outputs []chan int
    for i := 0; i < 2; i++ {
        ch := make(chan int)
        outputs = append(outputs, ch)
        go func(id int, c <-chan int) {
            for val := range c {
                fmt.Printf("Worker %d received %d\n", id, val)
            }
        }(i, ch)
    }
    go func() {
        for i := 0; i < 5; i++ {
            input <- i
        }
        close(input)
    }()
    distributor(input, outputs)
}

distributor函数将input channel的数据分发给多个output channel,不同的goroutine从各自的output channel接收数据。