在同步AVAudioEngine线程中有序调用Swift Actor的实现方案
问题场景
我正在使用Apple的AVAudioEngine编写如下代码:
var audioEngine = AVAudioEngine() audioEngine.inputNode.installTap { [buffer, when] in let waveform = myGetWaveform(buffer) Task { await Something.shared.doSomethingWith(waveform) } }
installTap的闭包会在音频线程上执行。由于Something是一个actor,我难以对其进行修改,因此采用了上述写法。但问题在于这些Task的执行顺序无法保证,而我需要它们按顺序执行。
这一问题也适用于任何无法将调用方改为异步的同步代码场景——我似乎找不到为异步Actor代码注入序列化逻辑的方法。我找到的诸多方案(如使用信号量等)似乎都违反了异步编程中“线程必须向前推进”的约定。
补充说明
- 之前遗漏了
await关键字,现已补上。 - 调用
inputNode.installTap方法时传入的回调闭包,会在inputNode启动后被频繁调用(每秒多次),buffer中包含从设备麦克风采样得到的音频波形数据。当前实现的问题在于,doSomethingWith会针对每个新音频缓冲区被反复调用,但调用顺序无法保证。
基于回答的实现
我根据Rob的回答实现了如下代码:
class AudioUploadQueue { private var continuation: AsyncStream<[Int16]>.Continuation? nonisolated lazy var stream: AsyncStream<[Int16]> = { AsyncStream(bufferingPolicy: .bufferingNewest(3)) { (continuation: AsyncStream<[Int16]>.Continuation) -> Void in self.continuation = continuation } }() func handleWaveformData(_ waveform: [Int16]) { self.continuation?.yield(waveform) } func stop() { self.continuation?.finish() } func monitorForWaveforms() async { for await waveform in stream { await AudioUploader.shared.handleAudioWaveform(waveform) } } }
我会在麦克风启动时实例化该类,停止时调用stop()并将实例置为nil。说实话这个实现有些繁琐,我惊讶于Swift没有更原生的解决方案,毕竟iOS中仍存在大量如AVAudioEngine这类非异步线程代码需要与应用其余代码交互。
内容的提问来源于stack exchange,提问作者Colin
相关产品推荐
相关产品推荐

