问题描述
任何进行过一些低级音频编程的人都被警告要锁定音频线程。 Here's一篇关于该主题的好文章。
但是我仍然不清楚如何真正地使用c ++制作多线程音频处理应用程序,同时严格遵循此规则并确保线程安全。假设您正在构建像可视化器这样的简单对象。您需要将音频数据交给UI线程进行处理,并定期显示。
我的第一个尝试是在两个缓冲区之间进行乒乓球。 buffer * _write_state是一个布尔类型,假定为原子型且无锁。 buffer *是某种类型的缓冲区,它不希望自己自己具有线程安全性,并且可以通过某种方式处理一个线程以不足的速率被调用的情况(在这里我并不打算陷入那种麻烦)。对于通用的布尔类型,实现如下所示:
// Write thread.
if (buffer1_write_state) {
buffer1.write(data);
if (buffer2_write_state) {
buffer1_write_state = false;
}
} else {
buffer2.write(data);
if (buffer1_write_state) {
buffer2_write_state = false;
}
}
// Read thread.
if (buffer1_write_state) {
data = buffer2.read();
buffer2.clear();
buffer2_write_state = true;
} else if (buffer2_write_state) {
data = buffer1.read();
buffer1.clear();
buffer1_write_state = true;
}
我已经使用std::atomic_flag作为我的布尔类型实现了这一点,并且据我的线程清理器所知,它是线程安全的。标准保证std :: atomic_flag是无锁的。令我感到困惑的是,要做到这一点,我还需要std :: atomic_flag的test()函数,该函数在c ++ 20之前是不存在的。可用的可变test_and_set()和clear()函数无法完成任务。标准不保证众所周知的替代方法std::atomic是无锁的。我听说大多数情况并非如此。
我读过一些线程,警告人们不要尝试自己尝试无锁结构,我很乐意遵守该技巧,但是如果没有基本工具,专家们将如何构建这些东西?保证没有锁吗?
解决方法
我听说大多数情况下不是。
您听错了。
std::atomic<bool>
在所有“常规” C ++实现中都是lock_free的,例如适用于ARM,x86,PowerPC等。如果atomic_flag
的限制性API过多,请使用它。或std::atomic<int>
,也普遍适用于具有无锁功能的目标上的lock_free。
(唯一合理的例外是8位计算机,无法加载/存储/ RMW一对字节。)
请注意,如果您以ARM为目标,则应启用编译器选项,以使您知道您不关心ARM CPU太旧而无法支持原子操作。在这种情况下,编译器将不得不在运行于ARMv4之类的 case 中编写使用库函数调用的慢速代码。参见std::atomic<bool> lock-free inconsistency on ARM (raspberry pi 3)