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

在同步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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:12:44