【问题标题】:Core Audio render thread and thread signallingCore Audio 渲染线程和线程信令
【发布时间】:2013-12-30 17:29:19
【问题描述】:

iOS 是否有任何不包括锁定的非常低级别的条件锁?

我正在寻找一种方法来从 Core Audio 渲染线程中发出等待线程的信号,而无需使用锁。我想知道是否可能存在像 Mach 系统调用这样的低级别的东西。

现在我有一个 Core Audio 线程,它使用非阻塞线程安全消息队列将消息发送到另一个线程。然后,另一个线程每 100 毫秒拉一次以查看队列中是否有消息。

但这是非常初级的,而且时机很糟糕。我可以使用条件锁,但这涉及到锁定,我希望将任何类型的锁定排除在渲染线程之外。

我正在寻找的是让消息队列线程等待,直到 Core Audio 渲染线程发出信号。就像 pthread 条件一样,但没有锁定并且没有立即的上下文切换?我希望 Core Audio 线程在消息队列线程被唤醒之前完成。

【问题讨论】:

  • 您想在没有任何锁定和/或上下文切换的情况下进行线程间信号传输?如果您正在排队音频缓冲区指针,为什么锁定和/或上下文切换会成为任何类型的瓶颈?
  • 这与音频渲染无关。这是关于核心音频线程发送正在发生的事情的消息:它将播放从一个音频缓冲区切换到另一个(轨道更改)或者它已经用完了音频帧来播放(缓冲区运行不足)等等。所以会发生什么,它将此消息放入一个结构中,然后将其写入非阻塞循环缓冲区。然后,另一个线程会不时(100 毫秒)检查缓冲区,作为回报,它会处理从渲染线程接收到的消息。所以我基本上只是在寻找一种方法来推送到另一个线程,而不是每 100 毫秒拉一次。
  • 如果您使用条件变量 (cnd_wait()),您的锁将非常短,即使对于音频渲染线程也是可以接受的。我将它用于 CoreMIDI readProc,但我也将它用于音频渲染线程。
  • @bio 我也这么认为,但事实证明,实时上下文中的任何锁定根本都是危险的。事件保证永不阻止对pthread_trylock() 的调用是不安全的,因为您锁定的任何东西都必须再次解锁,而解锁机制可能会导致系统调用,这反过来又会耗尽您的时间预算并导致音频捕捉。 (见:youtu.be/zrWYJ6FdOFQ?t=591

标签: ios multithreading core-audio


【解决方案1】:

更新

dispatch_semaphore_t 运行良好,并且比马赫semaphore_t 更有效。使用调度信号量的原始代码如下所示:

#include <dispatch/dispatch.h>

// Declare mSemaphore somewhere it is available to multiple threads
dispatch_semaphore_t mSemaphore;


// Create the semaphore
mSemaphore = dispatch_semaphore_create(0);
// Handle error if(nullptr == mSemaphore)


// ===== RENDER THREAD
// An event happens in the render thread- set a flag and signal whoever is waiting
/*long result =*/ dispatch_semaphore_signal(mSemaphore);


// ===== OTHER THREAD
// Check the flags and act on the state change
// Wait for a signal for 2 seconds
/*long result =*/ dispatch_semaphore_wait(mSemaphore, dispatch_time(dispatch_time_now(), 2 * NSEC_PER_SEC));


// Clean up when finished
dispatch_release(mSemaphore);

原答案:

您可以为此使用马赫semaphore_t。我编写了一个封装了功能的 C++ 类:https://github.com/sbooth/SFBAudioEngine/blob/master/Semaphore.cpp

无论您最终是使用我的包装器还是滚动您自己的包装器,代码大致如下:

#include <mach/mach.h>
#include <mach/task.h>

// Declare mSemaphore somewhere it is available to multiple threads
semaphore_t mSemaphore;


// Create the semaphore
kern_return_t result = semaphore_create(mach_task_self(), &mSemaphore, SYNC_POLICY_FIFO, 0);
// Handle error if(result != KERN_SUCCESS)


// ===== RENDER THREAD
// An event happens in the render thread- set a flag and signal whoever is waiting
kern_return_t result = semaphore_signal(mSemaphore);
// Handle error if(result != KERN_SUCCESS)


// ===== OTHER THREAD
// Check the flags and act on the state change
// Wait for a signal for 2 seconds
mach_timespec_t duration = {
  .tv_sec = 2,
  .tv_nsec = 0
};

kern_return_t result = semaphore_timedwait(mSemaphore, duration);

// Timed out
if(KERN_OPERATION_TIMED_OUT != result)
  ;

// Handle error if(result != KERN_SUCCESS)


// Clean up when finished
kern_return_t result = semaphore_destroy(mach_task_self(), mSemaphore);
// Handle error if(result != KERN_SUCCESS)

【讨论】:

  • 谢谢!这正是我想要的。如果可以的话,我会给你一千名声望。我知道会有一些我可以使用的低级东西。非常感谢! PS。会看看你的包装。
  • 使用无锁队列或循环 fifo 将实时音频回调与 UI 进行通信已被多个音频开发人员推荐。以显示帧速率(60Hz 或 16.6 毫秒或 CADisplayLink 为 1,而不是 100 毫秒)轮询将允许以最少线程数以全帧速率更新 UI。
  • @hotpaw2 这绝对是正确的,根据目的,即使以显示帧速率更新也可能过多(例如,当更新播放时间经常每秒 5 次就足够了)。
猜你喜欢
  • 1970-01-01
  • 2013-01-09
  • 2021-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多