后端实践选题方向三 | 豆包MarsCode AI 刷题

41 阅读3分钟

方向3 实践记录以及工具使用

goroutine之间的通信

在 Go 语言中,Goroutine 是一种轻量级的线程。Goroutine 之间的通信方式以 channel 为主,提供了一种线程安全、简洁易用的机制来实现协作和数据交换。

1. Goroutine 通信的主要方式

1.1 使用 Channel

Channel 是 Go 提供的内置类型,用于在多个 Goroutine 之间传递数据。它是线程安全的,并且通过 阻塞机制 实现同步。Channel 可以是无缓冲的(同步传输)或有缓冲的(异步传输)。

创建 Channel

ch := make(chan int)           // 无缓冲 Channel
ch := make(chan int, 5)        // 有缓冲 Channel,缓冲区大小为 5

发送和接收数据

ch <- value   // 发送数据到 channel
val := <-ch   // 从 channel 接收数据

关闭 Channel: 使用 close 关闭一个 channel,表示发送方不会再发送数据了。

close(ch)

例子:两个 Goroutine 之间的简单通信

package main
import "fmt"
func worker(ch chan int) {
    for {
        value, ok := <-ch
        if !ok { // channel 关闭,退出
            fmt.Println("Channel closed!")
            return
        }
        fmt.Println("Received:", value)
    }
}

func main() {
    ch := make(chan int)
    go worker(ch)
    for i := 1; i <= 5; i++ {
        ch <- i
    }
    close(ch) // 通知 worker Goroutine 数据传输完成
}

运行结果

Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Channel closed!

1.2 使用 Select 监听多个 Channel

select 是 Go 中用于监听多个 channel 的语法结构。它会阻塞,直到某个 channel 可以发送或接收数据。 例子:监听多个 channel

package main
import (
    "fmt"
    "time"
)
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "message from ch1"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "message from ch2"
    }()


    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received:", msg2)
        }
    }
}

运行结果

Received: message from ch2
Received: message from ch1

1.3 使用 Mutex 和共享变量

除了 Channel 外,Go 还提供了 sync.Mutex 来实现 Goroutine 之间的同步,保护共享资源。

例子:使用 Mutex 实现 Goroutine 间的安全共享

package main

import (
    "fmt"
    "sync"
)

var counter int
var mutex sync.Mutex

func worker(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        mutex.Lock()
        counter++
        mutex.Unlock()
    }
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(&wg)
    }
    wg.Wait()
    fmt.Println("Final Counter:", counter)
}

1.4 使用 WaitGroup 等待多个 Goroutine 完成

sync.WaitGroup 用于等待一组 Goroutine 完成任务。

例子:使用 WaitGroup 等待 Goroutine

package main

import (
    "fmt"
    "sync"
)


func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d started\n", id)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    wg.Wait() // 等待所有 Goroutine 完成
    fmt.Println("All workers completed!")

}

运行结果

Worker 1 started
Worker 1 done
Worker 2 started
Worker 2 done
...

All workers completed!

2. Goroutine 通信的常见模式

2.1 管道(Pipeline)模式 将数据处理拆分为多个阶段,每个阶段由 Goroutine 完成,数据通过 channel 在阶段之间传递。

例子

package main

import "fmt"


// Generator:生成一系列整数

func generator(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

// Squarer:平方计算

func squarer(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    nums := generator(2, 3, 4)
    squares := squarer(nums)

    for sq := range squares {
        fmt.Println(sq)
    }
}

运行结果

4
9
16

3. Channel 的一些高级用法

3.1 单向 Channel

发送专用:chan<- T

接收专用:<-chan T

例子

func send(ch chan<- int) {
    ch <- 42
}

func receive(ch <-chan int) {
    fmt.Println(<-ch)
}

func main() {
    ch := make(chan int)
    go send(ch)
    receive(ch)
}

3.2 带缓冲的 Channel

缓冲区允许 channel 存储多条数据,发送方无需等待接收方立即接收。

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch) // 输出: 1

个人总结

Channel 是 Goroutine 通信的核心工具,适合传递数据和实现同步。

select 可以监听多个 channel,让程序更具灵活性。

WaitGroup 和 Mutex 等工具补充了更细粒度的控制和同步。

• 高效使用 Goroutine 和通信机制需要结合场景,合理选择模型。