You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何以异步友好方式保护resetLobby临界区避免竞态条件?

解决Swift异步场景下的大厅监听器竞态问题

问题代码

func resetLobby() async {
    do {
        lobbyViewModel.pauseListener()
        let question = lobbyViewModel.currentLobby?.questionDeck?.currentQuestion
        if let winner, let question {
            print("Reset Lobby: ")
            try await lobbyViewModel.resetLobby(winner: winner, question: question)
        }
        try await lobbyViewModel.nextQuestion()
        lobbyViewModel.restartListener()
        // try await lobbyViewModel.nextQuestion()
        if let id = userViewModel.currentUser?.id, let num = lobbyViewModel.currentLobby?.number {
            try await userViewModel.loadUser(userId: id, lobbyNumber: num)
        }
    } catch {
        award = true
    }
}

问题描述

开发多用户大厅应用时,每个用户绑定了大厅状态更新的监听器。当前resetLobby()执行期间,大厅级监听器会被触发,导致返回给用户的属性信息错误——现有暂停/启动监听器仅对本地生效,无法控制全局监听器。需要一种Swift异步友好的方案,将resetLobby()标记为临界区,确保监听器在其执行完成后才触发。


解决方案

1. 用Swift Actor实现状态隔离(推荐)

Swift的Actor是官方原生的异步安全机制,能自动保证所有异步方法串行执行,从根源上避免竞态条件。将大厅核心逻辑封装到Actor中,所有状态修改和监听器触发都在Actor内部完成:

actor LobbyViewModel {
    private var currentLobby: Lobby?
    private var questionDeck: QuestionDeck?
    private var listeners: [LobbyStateListener] = []
    
    // 重置大厅核心逻辑
    func resetLobby(winner: Winner, question: Question) async throws {
        // 原重置逻辑实现
    }
    
    // 切换下一题逻辑
    func nextQuestion() async throws {
        // 原下一题逻辑实现
    }
    
    // 添加监听器
    func addListener(_ listener: LobbyStateListener) {
        listeners.append(listener)
    }
    
    // 仅在状态更新完成后通知监听器
    private func notifyListeners() async {
        guard let lobby = currentLobby else { return }
        for listener in listeners {
            await listener.onLobbyStateUpdated(lobby)
        }
    }
    
    // 原子化的重置流程
    func runResetFlow(winner: Winner?) async {
        do {
            let currentQuestion = currentLobby?.questionDeck?.currentQuestion
            if let winner, let question = currentQuestion {
                print("Reset Lobby: ")
                try await resetLobby(winner: winner, question: question)
            }
            try await nextQuestion()
            
            // 加载用户信息(如果依赖大厅状态,需在Actor内执行或保证串行)
            if let userId = UserViewModel.shared.currentUser?.id, let lobbyNum = currentLobby?.number {
                try await UserViewModel.shared.loadUser(userId: userId, lobbyNumber: lobbyNum)
            }
            
            // 所有操作完成后,统一触发监听器
            await notifyListeners()
        } catch {
            // 错误处理逻辑
        }
    }
}

说明:Actor会自动对内部状态的访问加锁,runResetFlow作为一个完整的原子操作执行,只有当所有重置步骤完成后,才会调用notifyListeners触发状态更新,彻底避免执行过程中监听器被触发的问题。

2. 异步锁实现临界区(轻量替代)

如果无法将整个ViewModel重构为Actor,可以实现一个简单的AsyncLock,将resetLobby()的逻辑包裹在临界区内:

class AsyncLock {
    private let semaphore = DispatchSemaphore(value: 1)
    
    func execute<T>(_ block: () async throws -> T) async rethrows -> T {
        semaphore.wait()
        defer { semaphore.signal() }
        return try await block()
    }
}

在原ViewModel中使用该锁:

class LobbyViewModel {
    private let asyncLock = AsyncLock()
    // 其他属性...
    
    func resetLobby() async {
        try? await asyncLock.execute { [weak self] in
            guard let self = self else { return }
            
            // 原resetLobby的所有逻辑
            let question = self.currentLobby?.questionDeck?.currentQuestion
            if let winner, let question {
                print("Reset Lobby: ")
                try await self.resetLobby(winner: winner, question: question)
            }
            try await self.nextQuestion()
            
            if let id = self.userViewModel.currentUser?.id, let num = self.currentLobby?.number {
                try await self.userViewModel.loadUser(userId: id, lobbyNumber: num)
            }
        }
        
        // 所有临界区操作完成后,手动触发监听器(或确保监听器仅读取最终状态)
        self.notifyListeners()
    }
    
    private func notifyListeners() {
        // 触发监听器逻辑
    }
}

说明:AsyncLock通过信号量保证临界区内的代码串行执行,任何尝试访问大厅状态的操作(包括监听器的状态读取)都会等待锁释放,确保监听器看到的是重置完成后的最终状态。


内容的提问来源于stack exchange,提问作者lionsfan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:07:43