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

Swift中按需等待异步结果的正确实现(解决信号量死锁)

问题分析与解决方案

兄弟,你这代码踩了信号量在异步场景下的经典坑——死锁!我来给你拆解一下问题,再给你几个靠谱的实现方式:

为什么会无限等待?

假设你在主队列调用getTaskId,而getGroupId的回调也是在主队列执行的话:

  • 你调用semaphore.wait()会直接阻塞当前的主队列线程
  • 这时候getGroupId的回调代码根本没机会在主队列上执行,自然semaphore.signal()永远不会被调用
  • 结果就是信号量一直等下去,陷入死锁

就算不是主队列,如果getGroupId的回调和wait()在同一个串行队列,也会出现同样的问题。信号量这种方式只适合在不同队列之间同步,不能在同一个串行队列里这么玩。

正确的实现方式

方式1:用回调嵌套(兼容所有Swift版本)

把后续依赖groupId的逻辑抽成一个单独的函数,不管是异步拿到groupId还是直接传入的,都统一调用这个函数继续执行,完全用异步回调的方式处理,避免阻塞线程:

func getTaskId(uid: String, groupName: String?, groupId: String?, taskName: String, handler: @escaping ([String]?, Error?) -> Void) {
    // 把需要用groupId执行的逻辑抽出来
    func proceedWithValidGroupId(_ finalGroupId: String) {
        // 这里放你原来在semaphore.wait()之后的代码,比如获取taskId的逻辑
        // 举个例子:假设你有个异步获取taskId的方法
        fetchTaskIds(forGroupId: finalGroupId, taskName: taskName) { taskIds, fetchErr in
            if let err = fetchErr {
                handler(nil, err)
                return
            }
            handler(taskIds, nil)
        }
    }

    // 处理groupId的获取逻辑
    if let groupName = groupName {
        getGroupId(uid: uid, groupName: groupName) { fetchedGroupId, groupErr in
            if let err = groupErr {
                handler(nil, err)
                return
            }
            guard let validGroupId = fetchedGroupId else {
                let err = xErrors.getTaskIdErr(message: "Failed to get group id")
                handler(nil, err)
                return
            }
            proceedWithValidGroupId(validGroupId)
        }
    } else if let providedGroupId = groupId {
        proceedWithValidGroupId(providedGroupId)
    } else {
        let err = xErrors.getTaskIdErr(message: "Failed to get group id")
        handler(nil, err)
    }
}

方式2:用Async/Await(Swift 5.5+ 首选)

如果你的项目已经用上了Swift 5.5及以上版本,async/await是最简洁的方案,完全摆脱回调地狱和死锁风险:

首先把回调式的getGroupId包装成async函数:

// 假设getGroupId是某个类的方法,这里给它加个async扩展
extension YourGroupService { // 替换成getGroupId所属的实际类名
    func getGroupId(uid: String, groupName: String) async throws -> String {
        return try await withCheckedThrowingContinuation { continuation in
            self.getGroupId(uid: uid, groupName: groupName) { groupId, err in
                if let err = err {
                    continuation.resume(throwing: err)
                } else if let groupId = groupId {
                    continuation.resume(returning: groupId)
                } else {
                    continuation.resume(throwing: xErrors.getTaskIdErr(message: "Failed to get group id"))
                }
            }
        }
    }
}

然后把getTaskId重写成async函数:

func getTaskId(uid: String, groupName: String?, groupId: String?, taskName: String) async throws -> [String] {
    let finalGroupId: String
    
    // 确定最终的groupId
    if let groupName = groupName {
        finalGroupId = try await getGroupId(uid: uid, groupName: groupName)
    } else if let providedId = groupId {
        finalGroupId = providedId
    } else {
        throw xErrors.getTaskIdErr(message: "Failed to get group id")
    }
    
    // 同样把获取taskId的回调方法也包装成async的,然后调用
    return try await fetchTaskIds(forGroupId: finalGroupId, taskName: taskName)
}

这样代码逻辑清晰,可读性拉满,还完全避免了同步阻塞带来的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:33:13