MST

星途 面试题库

面试题:Go WaitGroup在复杂并发场景中的应用

假设有一个复杂的并发任务场景,需要启动多个不同类型的goroutine,这些goroutine之间存在依赖关系,部分goroutine完成后才能启动另一部分。请使用WaitGroup设计一个解决方案来管理这些goroutine的执行顺序,并阐述设计思路及在这个过程中WaitGroup使用的要点和可能遇到的陷阱。
34.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 定义任务依赖关系:明确哪些goroutine需要等待其他goroutine完成后才能启动。
  2. 使用WaitGroup:为每个需要等待的阶段创建一个WaitGroup实例。当一个goroutine完成任务时,调用WaitGroup的Done方法;需要等待一组goroutine完成的goroutine则调用Wait方法。
  3. 启动goroutine:按照依赖关系顺序启动各个goroutine。

示例代码

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg1, wg2 sync.WaitGroup

    // 启动第一组goroutine
    wg1.Add(2)
    go func() {
        defer wg1.Done()
        // 模拟任务1
        fmt.Println("Task 1 completed")
    }()
    go func() {
        defer wg1.Done()
        // 模拟任务2
        fmt.Println("Task 2 completed")
    }()

    // 等待第一组goroutine完成
    go func() {
        wg1.Wait()
        // 启动第二组goroutine
        wg2.Add(1)
        go func() {
            defer wg2.Done()
            // 模拟任务3,依赖任务1和任务2完成
            fmt.Println("Task 3 completed")
        }()
    }()

    // 等待所有任务完成
    wg2.Wait()
    fmt.Println("All tasks completed")
}

WaitGroup使用要点

  1. 初始化:在使用WaitGroup之前,需要通过Add方法设置需要等待的goroutine数量。
  2. 调用Done:每个完成的goroutine都应在结束前调用WaitGroupDone方法,标记自己已完成任务。
  3. 调用Wait:需要等待一组goroutine完成的goroutine应调用Wait方法,阻塞直到所有相关goroutine都调用了Done

可能遇到的陷阱

  1. 忘记调用Add:如果没有调用Add方法设置正确的等待数量,Wait方法可能会提前返回或永远阻塞。
  2. 重复调用Add:在Wait方法已经被调用后再次调用Add,会导致panic。所以确保Add只在Wait之前调用。
  3. 忘记调用Done:如果有goroutine没有调用DoneWait方法将永远阻塞,导致程序无法继续执行。
  4. 使用不当的并发场景:在多个地方对同一个WaitGroup进行操作时,要注意并发安全,避免数据竞争。