MST

星途 面试题库

面试题:Go的WaitGroup在复杂并发场景下的资源管理

假设你正在开发一个游戏服务器,有多个玩家连接,每个玩家会有一系列并发任务,比如处理玩家的移动、攻击等操作。在这些并发任务中,有一些共享资源(例如服务器的总积分)需要在所有玩家的相关任务完成后进行更新。请使用Go语言和WaitGroup设计一个合理的并发模型来处理这种情况,并阐述如何避免共享资源的竞争条件,以及WaitGroup在其中是如何协调任务顺序的。
27.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

Go语言代码示例

package main

import (
    "fmt"
    "sync"
)

// 定义服务器总积分
var totalScore int

func playerTask(playerID int, wg *sync.WaitGroup) {
    defer wg.Done()
    // 模拟玩家的移动、攻击等操作
    fmt.Printf("Player %d is performing tasks...\n", playerID)
    // 假设这些操作完成后会对总积分有影响,这里简单模拟增加10分
    // 注意这里需要加锁防止竞争条件
    mutex.Lock()
    totalScore += 10
    mutex.Unlock()
}

var mutex sync.Mutex

func main() {
    numPlayers := 5
    var wg sync.WaitGroup
    wg.Add(numPlayers)

    for i := 1; i <= numPlayers; i++ {
        go playerTask(i, &wg)
    }

    wg.Wait()
    fmt.Printf("All player tasks completed. Total score: %d\n", totalScore)
}

避免共享资源竞争条件的方法

  1. 使用互斥锁(Mutex):在对共享资源(如totalScore)进行读写操作时,通过mutex.Lock()mutex.Unlock()来确保同一时间只有一个协程能够访问共享资源。这样就避免了多个协程同时修改共享资源导致的数据不一致问题。
  2. 读写锁(RWMutex):如果读操作远多于写操作,可以考虑使用读写锁。读操作时允许多个协程同时进行,写操作时则独占资源,从而提高并发性能。

WaitGroup协调任务顺序的原理

  1. Add方法:在启动每个玩家的并发任务前,通过wg.Add(numPlayers)将WaitGroup的计数器设置为玩家数量,即需要等待完成的任务数。
  2. Done方法:在每个玩家任务函数playerTask中,使用defer wg.Done(),表示该任务已经完成,会将WaitGroup的计数器减1。
  3. Wait方法wg.Wait()会阻塞主线程,直到WaitGroup的计数器变为0,即所有玩家的任务都调用了wg.Done()。这样就确保了在所有玩家的相关任务完成后,才会继续执行后续对共享资源更新的操作(在示例代码中就是打印总积分)。