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

命令行应用中DispatchSemaphore未正常等待问题求助(无需RunLoop.main.run())

问题分析

你的代码存在两个核心问题:

  • synthesizer.speak(utterance)是异步执行的,你在调用它之后立刻调用sema.signal(),导致信号量还没等到语音播放完成就被释放,完全起不到等待作用。
  • 命令行环境下,AVFoundation的语音合成依赖RunLoop处理音频播放的回调和事件,没有RunLoop的话,语音合成任务根本无法正常启动执行。
解决方案

要实现等待语音播放完成的逻辑,需要结合AVSpeechSynthesizerDelegate监听播放完成事件,同时通过临时运行RunLoop驱动任务,避免调用RunLoop.main.run()持续阻塞。

修改后的代码示例

import Foundation
import AVFoundation

class SpeechHandler: NSObject, AVSpeechSynthesizerDelegate {
    let synthesizer = AVSpeechSynthesizer()
    let sema = DispatchSemaphore(value: 0)
    
    func say(_ line: String) {
        synthesizer.delegate = self
        let utterance = AVSpeechUtterance(string: line)
        utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN") // 可选:指定语音语种
        synthesizer.speak(utterance)
    }
    
    // 代理方法:语音播放完成时触发信号量
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        sema.signal()
    }
}

let handler = SpeechHandler()
handler.say("hello")

// 临时运行RunLoop,直到信号量被触发
while handler.sema.wait(timeout: .now() + 0.1) == .timedOut {
    RunLoop.main.run(until: Date().addingTimeInterval(0.1))
}

代码说明

  • 通过AVSpeechSynthesizerDelegate的didFinish回调,确保只有在语音播放完成后才释放信号量,真正实现等待逻辑。
  • 用循环结合RunLoop.main.run(until:)的方式,每次仅运行RunLoop 0.1秒,直到信号量被触发,避免持续阻塞主RunLoop。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:28:23