【问题标题】:AudioUnit callback and synchronization: how to ensure thread safety with GCDAudioUnit 回调和同步:如何使用 GCD 确保线程安全
【发布时间】:2020-02-19 02:08:01
【问题描述】:

我正在构建一个基于 AudioUnit 回调工具和音频处理节点图的音频应用程序。我知道回调是在单独的(高优先级?)线程中执行的,因此与我的处理节点的所有交互,例如播放时更改 EQ 参数应以线程安全的方式进行。换句话说,在执行音频回调链时,应保护节点不被修改。

我对更底层的多线程的理解是,我需要在每个节点中使用一个锁,或者为整个图形设置一个锁,以防止在处理音频缓冲区时写入。

但是,我希望实现更加“Swifty”并使用应该提供上述功能的DispatchQueue/DispatchGroup。我只是不太明白如何以最有效的方式做到这一点。

假设所有音频参数的修改都在一个队列中完成,如下所示:

audioQueue.async {
    eqNode.setEqParameters(...)
}

如何确保在 AudioUnit 回调完成之前不会执行此块?使用audioQueue.sync 不是一个选项,因为这意味着系统音频线程将依赖于我的audioQueue,这不是很好。

如果我使用DispatchGroup 实现上述流程的最佳方式是什么?

【问题讨论】:

    标签: swift multithreading grand-central-dispatch audiounit dispatch-queue


    【解决方案1】:

    实时音频单元回调不应锁定、等待锁定或管理内存(Swift 或 Objective C 对象或方法)。

    我会双重缓冲音频参数集(或更多,例如参数集的环形缓冲区)。让作者锁定正在更改的集合,完成后解锁,并且永远不要以或超过已知音频回调速率(慢 2 倍以上是安全的)切换集合。让周期性阅读器(音频单元实时回调)检查锁定,而不使用锁定的参数集。

    为了不在线程间环形缓冲区上使用锁,您可能希望在指针、索引或状态加载/存储上使用 OS 原子内存屏障:load_acquire、store_release 等,以防止 ARM 处理器写入缓冲区重新排序或 Swift 优化器指令重新排序,以免损坏您的数据。

    【讨论】:

    • 谢谢。我想我找到了一个稍微好一点的解决方案:它是一个线程安全队列的模仿,队列中最多有一个元素。在每个音频周期开始时,我会查看队列中是否有内容并将其复制/出列。但它不是一个真正的队列,只是一个指向受信号量保护的参数块的可选指针。
    • 我为此使用 DispatchSemaphore,尽管现在我认为一些 OSAtomic* 函数也可以完成这项工作。
    猜你喜欢
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多