MST

星途 面试题库

面试题:Go并发编程中WaitGroup的复杂应用场景分析

假设你需要在Go程序中启动多个并发任务,这些任务之间存在依赖关系,例如任务A和B需先完成,然后才能开始任务C。请使用WaitGroup来设计一个合理的并发模型,并解释你代码中如何通过WaitGroup控制任务的执行顺序以及避免常见的并发错误。
22.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    // 为任务A、B、C添加计数
    wg.Add(3)

    // 任务A
    go func() {
        defer wg.Done()
        fmt.Println("任务A开始执行")
        // 模拟任务A的工作
        fmt.Println("任务A执行完毕")
    }()

    // 任务B
    go func() {
        defer wg.Done()
        fmt.Println("任务B开始执行")
        // 模拟任务B的工作
        fmt.Println("任务B执行完毕")
    }()

    // 任务C
    go func() {
        // 等待任务A和B完成
        wg.Wait()
        defer wg.Done()
        fmt.Println("任务C开始执行")
        // 模拟任务C的工作
        fmt.Println("任务C执行完毕")
    }()

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

代码说明

  1. WaitGroup的使用

    • wg.Add(3):初始化WaitGroup的计数器为3,代表有三个任务(A、B、C)。
    • 每个任务函数中都有defer wg.Done(),表示任务完成时将计数器减1。
    • 在任务C中,wg.Wait()会阻塞任务C的执行,直到WaitGroup的计数器归零,即任务A和B都完成。
    • 最后在main函数末尾再次调用wg.Wait(),确保所有任务都执行完毕后程序才退出。
  2. 避免常见并发错误

    • 死锁:通过正确设置wg.Addwg.Donewg.Wait的位置避免死锁。例如,如果在任务C中没有wg.Wait(),任务C可能在任务A和B未完成时就开始执行,导致不符合依赖关系。如果没有在每个任务中调用wg.Done()wg.Wait()可能永远阻塞,造成死锁。
    • 数据竞争:在这段代码中,每个任务相对独立,不存在对共享资源的读写操作,所以没有数据竞争问题。如果存在共享资源,需要使用互斥锁(如sync.Mutex)来保护共享资源,避免数据竞争。