MST
星途 面试题库

面试题:Go WaitGroup与其他并发原语在大型项目中的协作应用

在一个大型分布式系统中,有多个服务节点,每个节点上有多个Go协程负责不同的任务,如数据采集、数据处理、数据传输等。不同类型的任务之间存在依赖关系,例如数据处理任务需要等待数据采集任务完成。同时,为了资源的合理利用,需要限制同一时间运行的协程数量。请设计一个基于WaitGroup以及其他合适的Go并发原语(如Mutex、Channel等)的方案,来实现这个复杂的并发任务调度与管理,并阐述该方案在性能、可靠性等方面的优势和潜在问题。
24.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现方案

  1. 使用WaitGroup控制任务完成同步WaitGroup用于等待一组协程完成任务。对于存在依赖关系的任务,例如数据处理任务等待数据采集任务完成,可以让数据采集任务完成后调用wg.Done(),数据处理任务在开始前调用wg.Wait()
  2. 使用Channel控制并发数量:创建一个带缓冲的Channel,其缓冲区大小即为允许同时运行的最大协程数量。每个协程在启动前尝试从该Channel获取一个值,任务完成后再向Channel发送一个值,这样就可以限制同一时间运行的协程数量。
  3. 使用Mutex保护共享资源:如果不同任务需要访问共享资源(如共享的数据结构),则使用Mutex来确保数据的一致性,避免竞态条件。

以下是示例代码:

package main

import (
    "fmt"
    "sync"
)

// 限制同时运行的协程数量
const maxConcurrent = 3

func main() {
    var wg sync.WaitGroup
    semaphore := make(chan struct{}, maxConcurrent)

    // 模拟数据采集任务
    for i := 0; i < 5; i++ {
        semaphore <- struct{}{} // 获取信号量
        wg.Add(1)
        go func(id int) {
            defer func() {
                <-semaphore // 释放信号量
                wg.Done()
            }()
            fmt.Printf("Data collection task %d is running\n", id)
            // 模拟数据采集操作
        }(i)
    }

    // 等待所有数据采集任务完成
    wg.Wait()

    // 模拟数据处理任务,依赖数据采集任务完成
    for i := 0; i < 3; i++ {
        semaphore <- struct{}{} // 获取信号量
        wg.Add(1)
        go func(id int) {
            defer func() {
                <-semaphore // 释放信号量
                wg.Done()
            }()
            fmt.Printf("Data processing task %d is running\n", id)
            // 模拟数据处理操作
        }(i)
    }

    // 等待所有数据处理任务完成
    wg.Wait()
}

性能优势

  1. 资源合理利用:通过限制并发协程数量,可以避免系统资源过度消耗,例如CPU和内存,提高系统整体性能。
  2. 高效的同步机制WaitGroupChannel的使用提供了高效的同步机制,能够准确控制任务的执行顺序和并发数量,减少不必要的等待时间。

可靠性优势

  1. 数据一致性Mutex的使用确保了共享资源的安全访问,避免了竞态条件,从而保证了数据的一致性和可靠性。
  2. 任务依赖管理:通过WaitGroup实现任务之间的依赖关系,确保依赖的任务完成后再执行后续任务,提高了系统的稳定性。

潜在问题

  1. 死锁风险:如果在使用WaitGroupChannelMutex时逻辑错误,例如忘记调用wg.Done()或在错误的地方获取/释放信号量,可能会导致死锁。
  2. 性能瓶颈:虽然限制并发数量有助于资源管理,但如果设置的并发数量过小,可能会导致任务执行时间过长,形成性能瓶颈。
  3. 代码复杂性:使用多种并发原语会增加代码的复杂性,使得代码的理解、调试和维护变得更加困难。