gpt4 book ai didi

c++ - 什么时候需要无锁数据结构来跨线程读取/写入音频应用程序中的数据?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:38:42 26 4
gpt4 key购买 nike

我的场景是这样的:用户与 GUI 元素交互,音频回调函数读取 UI 设置的变量,计算样本并将样本存储在缓冲区(或任何数据结构)中,缓冲区然后由 UI 读取并绘制波形(在绘制循环中每秒 60 次)。

现在,根据我读过的一些资料(Linux 音频开发列表中的一个线程,thisthis),我需要某种无需锁定即可同时读取和写入的数据结构,或者,我需要某种跨线程通知系统来传递变量。

然而,some examples我见过使用 C++ std 库中的普通 vector ,它们从一个线程读取并从另一个线程写入,当我运行程序时,它们运行良好。

  1. 在哪些情况下我需要使用无锁数据结构来进行这种跨线程通信?
  2. 如果我添加另一个线程(例如 MIDI 或 OSC 回调函数)来接收网络 IO 并需要将数据传递给其他两个线程,我是否需要担心无锁结构?
  3. 如果第二个问题的答案是"is",那么使用哪种结构比较合适?

最佳答案

如果您有访问同一内存(读取或写入)的线程,那么您要么需要使用锁,要么需要使用无锁数据结构。否则,当多个线程同时访问您的数据结构时,它们可能会损坏(或看起来已损坏)。

您指向的示例似乎使用了提前分配的固定大小 vector 。音频线程正在写入此缓冲区,而 UI 线程正在读取它,两者不同步。由于两者可以完全并发运行,因此 UI 线程无法保证实际读取的是什么数据;它可能从更新 N 读取一些数据,从更新 N+1 读取一些数据。它可能会遗漏某些数据或读取某些数据两次(或更多次)。这不是构建音频应用程序的可靠方法。对于一个简单的可视化应用程序来说,它“工作”得很好,因为可视化的结果不需要是完美的,但它完全不适合录制或回放应用程序。

音频应用程序经常使用无锁数据结构(而不​​是使用锁),因为音频播放具有“实时”要求。如果您的音频缓冲区包含 100 毫秒的声音,那么您需要每秒填充这些缓冲区 10 次,否则您的音频播放会断断续续。另一种说法是,您每次都有 100 毫秒的截止时间 来填充缓冲区。

有各种各样的事情会导致您错过这个 100 毫秒的截止日期;如果系统太忙,您的进程可能无法安排,或者页面错误可能导致磁盘读取阻塞进程太长时间,仅举几个例子。如果您尝试获取锁但另一个线程持有它超过 100 毫秒,这将使您错过最后期限。这就是为什么使用锁对音频应用程序不利的原因;另一个持有锁太久的线程可能会让您错过最后期限。

使用无锁数据结构,无需等待锁,因此其他线程无法停止您的进程。它可以让您更轻松地完成音频 I/O 截止日期。

但是在您对无锁算法感到兴奋之前,您应该知道它们比锁更微妙并且需要更多的专业知识才能正确使用。基本上,如果您不是该领域的专家,则不应尝试自己编写无锁算法。也许有一个很好的开源库,它有一些无锁算法实现;我最近没看。

但是请注意,除非您在音频线程中也非常小心以避免其他可能的延迟原因,否则使用无锁算法所需的额外工作或多或少会被浪费。具体来说:

  • 您的音频线程不得 malloc()free() 任何内存(大多数 malloc/free 实现在内部获取全局锁)

  • 您必须确保您的音频线程访问的任何内存都没有被调出(例如使用 mlock())

  • 除了声卡之外,您的音频线程不得执行任何 I/O,因为 I/O 调用可能会阻塞。

除非您非常勤奋并采取了所有这些步骤,否则您还不如对与其他线程共享的数据使用锁,但要确保锁的持有时间非常短。例如,确保不要在持有锁的 UI 线程中执行任何 malloc()/free() 或阻塞系统调用。

关于c++ - 什么时候需要无锁数据结构来跨线程读取/写入音频应用程序中的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9255282/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com