MST
星途 面试题库

面试题:Go并发场景下信号量与WaitGroup的结合运用

在一个复杂的Go并发任务中,有多个协程需要并发执行,同时每个协程在执行时又受到信号量的限制,只能有特定数量的协程同时执行某部分操作。任务结束后需要等待所有协程完成。请设计一个方案并编写代码实现,阐述信号量和WaitGroup在其中分别起到什么作用,以及如何协同工作。
27.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

方案设计及代码实现

  1. 信号量实现:在Go语言中,我们可以使用 sync.Mutexsync.Cond 来实现信号量,或者直接使用 golang.org/x/sync/semaphore 包。这里我们使用 semaphore 包。
  2. WaitGroup使用sync.WaitGroup 用于等待一组协程完成。我们在创建协程时调用 wg.Add(1),在协程结束时调用 wg.Done(),最后在主协程中调用 wg.Wait() 等待所有协程完成。
package main

import (
    "context"
    "fmt"
    "golang.org/x/sync/semaphore"
    "sync"
    "time"
)

func main() {
    // 最大并发数
    maxConcurrent := 3
    var wg sync.WaitGroup
    sem := semaphore.NewWeighted(int64(maxConcurrent))

    // 模拟10个并发任务
    taskCount := 10
    for i := 0; i < taskCount; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            // 获取信号量
            err := sem.Acquire(context.Background(), 1)
            if err != nil {
                fmt.Printf("任务 %d 获取信号量失败: %v\n", id, err)
                return
            }
            defer sem.Release(1)

            // 模拟任务执行
            fmt.Printf("任务 %d 开始执行\n", id)
            time.Sleep(time.Second)
            fmt.Printf("任务 %d 执行结束\n", id)
        }(i)
    }

    // 等待所有任务完成
    wg.Wait()
    fmt.Println("所有任务已完成")
}

信号量和WaitGroup的作用及协同工作

  1. 信号量的作用:信号量在这里用于限制同时执行某部分操作的协程数量。在上述代码中,semaphore.NewWeighted(int64(maxConcurrent)) 创建了一个最大并发数为 maxConcurrent 的信号量。每个协程在执行关键操作前通过 sem.Acquire(context.Background(), 1) 获取信号量,如果信号量不足则等待,操作完成后通过 sem.Release(1) 释放信号量,这样就保证了同时执行的协程数量不会超过 maxConcurrent
  2. WaitGroup的作用sync.WaitGroup 用于同步主协程和其他并发协程。主协程调用 wg.Wait() 进入阻塞状态,直到所有协程都调用了 wg.Done(),从而确保所有协程完成后程序才结束。
  3. 协同工作:信号量控制并发执行的协程数量,而WaitGroup确保主协程等待所有协程完成。在每个协程开始时,我们既增加WaitGroup的计数,又获取信号量,协程结束时释放信号量并减少WaitGroup的计数。这样,信号量和WaitGroup共同工作,既保证了并发的合理性,又保证了任务的完整性。