命令行应用中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
相关产品推荐
相关产品推荐

