MST

星途 面试题库

面试题:Go中WaitGroup在带有依赖关系的并发场景的应用

现在有一个任务依赖关系图,任务A需要先执行,任务B和C依赖任务A完成后才能执行,任务D依赖任务B和C完成后执行。请使用Go的WaitGroup设计并实现这个复杂并发场景的程序,描述如何通过WaitGroup来管理任务间的依赖关系以及如何避免死锁。
47.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    // 为所有任务计数
    wg.Add(4)

    // 执行任务A
    go func() {
        defer wg.Done()
        fmt.Println("Task A is running")
        // 模拟任务A执行
    }()

    // 执行任务B,依赖任务A
    go func() {
        // 这里等待任务A完成(可通过channel等方式,这里简单示意任务A执行快则不等待)
        wg.Wait()
        defer wg.Done()
        fmt.Println("Task B is running")
        // 模拟任务B执行
    }()

    // 执行任务C,依赖任务A
    go func() {
        // 这里等待任务A完成(可通过channel等方式,这里简单示意任务A执行快则不等待)
        wg.Wait()
        defer wg.Done()
        fmt.Println("Task C is running")
        // 模拟任务C执行
    }()

    // 执行任务D,依赖任务B和C
    go func() {
        // 等待任务B和C完成
        wg.Wait()
        defer wg.Done()
        fmt.Println("Task D is running")
        // 模拟任务D执行
    }()

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

通过WaitGroup管理任务间依赖关系

  1. 初始化计数:在main函数开始,通过wg.Add(4)为4个任务(A、B、C、D)设置初始计数。
  2. 任务完成通知:每个任务在执行结束前调用wg.Done(),表示该任务完成,将WaitGroup的计数减1。
  3. 等待依赖任务:例如任务B和C需要等待任务A完成,任务D需要等待任务B和C完成,它们通过wg.Wait()阻塞,直到依赖任务调用wg.Done()使计数为0。

避免死锁

  1. 合理设置计数:确保wg.Add()的调用次数和实际任务数量匹配,避免因计数过少导致某些任务未被等待,或计数过多导致wg.Wait()永远阻塞。
  2. 及时调用wg.Done():每个任务必须在结束时调用wg.Done(),若遗漏会导致wg.Wait()一直等待,从而产生死锁。
  3. 避免循环依赖:在设计任务依赖关系时,要避免出现任务间循环依赖的情况,否则会导致死锁。例如,不能出现A依赖B,B依赖C,C又依赖A的情况。