如果您有线程正在访问相同的内存(读取或写入),那么您要么需要使用锁,要么需要使用无锁数据结构。否则,当多个线程同时访问您的数据结构时,它们可能会损坏(或看起来已损坏)。
您指向的示例似乎使用了提前分配的固定大小的向量。音频线程正在写入这个缓冲区,而 UI 线程正在读取它,两者并不同步。由于两者可以完全同时运行,UI 线程无法保证实际读取的是什么数据;它可能会从更新 N 中读取一些数据,从更新 N+1 中读取一些数据。它可能会丢失一些数据或两次(或更多)读取一些数据。这不是构建音频应用程序的可靠方法。它对于一个简单的可视化应用程序来说已经足够“工作”了,因为可视化结果不需要完美,但它完全不适合录制或回放应用程序。
音频应用程序经常使用无锁数据结构(而不是使用锁),因为音频播放具有“实时”要求。如果您的音频缓冲区包含 100 毫秒的声音,那么您需要每秒填充这些缓冲区 10 次,否则您的音频播放会卡顿。另一种说法是您每次 都有 100 毫秒的期限来填充缓冲区。
有各种各样的事情会导致你错过这 100 毫秒的最后期限;如果系统太忙,您的进程可能无法安排,或者页面错误可能导致磁盘读取阻塞进程太久,仅举几个例子。如果您尝试获取锁但另一个线程持有它超过 100 毫秒,这将使您错过最后期限。这就是为什么使用锁对音频应用程序不利的原因。另一个持有锁太久的线程会让你错过最后期限。
使用无锁数据结构,无需等待锁,因此其他线程无法停止您的进度。它使您的音频 I/O 截止日期变得更容易。
但在您对无锁算法过于兴奋之前,您应该知道它们比锁更微妙并且需要更多的专业知识才能正确使用。基本上,如果您不是该领域的专家,您不应该尝试自己编写无锁算法。也许有一个很好的开源库,它有一些无锁算法实现;最近没看
但是要意识到使用无锁算法所花费的额外工作或多或少是浪费的,除非您在音频线程中也非常小心以避免其他可能的延迟原因。具体来说:
您的音频线程不得malloc()
或free()
任何内存(大多数 malloc/free 实现在内部获取全局锁)
您必须确保您的音频线程访问的任何内存都没有被分页(例如mlock()
)
除了声卡之外,您的音频线程不得执行任何 I/O,因为 I/O 调用可能会阻塞。
除非您非常勤奋并采取所有这些步骤,否则您最好对与其他线程共享的数据使用锁,但请确保锁的持有时间非常短。例如,确保不要在持有锁的 UI 线程中执行任何 malloc()/free() 或阻塞系统调用。