没有优先级反转的环形缓冲区
ring buffer without priority inversion
我有一个高优先级进程,它需要将数据传递给一个低优先级进程。我已经写了一个基本的环缓冲区来处理数据的传递:
class RingBuffer {
public:
RingBuffer(int size);
~RingBuffer();
int count() {return (size + end - start) % size;}
void write(char *data, int bytes) {
// some work that uses only buffer and end
end = (end + bytes) % size;
}
void read(char *data, int bytes) {
// some work that uses only buffer and start
start = (start + bytes) % size;
}
private:
char *buffer;
const int size;
int start, end;
};
问题在这里。假设低优先级进程有一个oracle,可以告诉它需要读取多少数据,因此永远不需要调用count()
。然后(除非我遗漏了什么)就没有并发问题了。然而,一旦低优先级线程需要调用count()
(高优先级线程可能也想调用它来检查缓冲区是否太满),count()中的数学运算或到结束的更新就有可能不是原子性的,从而引入错误。
我可以在开始和结束的访问周围放一个互斥锁,但是如果高优先级线程必须等待低优先级线程获得锁,那么这会导致优先级反转。
我可能能够使用原子操作,但我不知道有一个很好的,跨平台的库提供这些。
是否有一个标准的环缓冲区设计来避免这些问题?
只要你遵守这些准则,你所拥有的应该是可以的:
- 只有一个线程可以写。只有一个线程可以做读操作。
- 对
start
和end
的更新和访问是原子的。这可能是自动的,例如Microsoft声明:
简单的读写正确对齐的32位变量是原子操作。换句话说,就是你最后不会只吃一份吗更新的变量的;所有比特都是以原子方式更新。
- 你允许
count
可能过时的事实,即使你得到的值。在读取线程中,count
将返回您可以依赖的最小计数;对于写线程count
将返回最大计数,而真实计数可能更低。
Boost提供了一个循环缓冲区,但它不是线程安全的。不幸的是,我不知道有任何实现是这样的。
即将发布的c++标准将原子操作添加到标准库中,因此它们将在未来可用,但大多数实现尚未支持它们。
我没有看到任何跨平台的解决方案来保持count
正常,而两个线程都写它,除非你实现锁定。
通常,您可能会使用消息传递系统,并强制低优先级线程请求高优先级线程进行更新,或类似的操作。例如,如果低优先级线程消耗15字节,它应该要求高优先级线程将计数减少15。
本质上,你将限制高优先级线程的写访问,只允许低优先级线程的读。这样,您就可以避免所有锁定,高优先级线程不必担心等待较低线程完成写操作,从而使高优先级线程真正具有高优先级。
boost::interprocess
在boost/interprocess/detail/atomic.hpp
中提供跨平台原子函数
- C++字符*缓冲区的大小
- 为什么msgrcv()将垃圾字符馈送到缓冲区
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- ostream过载时的缓冲区冲洗
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- 按对象的特定方法按升序排列的C++优先级队列
- 使用2个键的cpp-stl::优先级队列排序不正确
- Xaudio2在更改缓冲区或循环时弹出声音
- 为什么我在leetcode上收到AddressSanitizer:地址0x602000000058上的堆缓冲区溢出错误
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- 如何在cpp.中使用协议缓冲区存储大缓冲区/数组(char/int)
- 多线程双缓冲区
- Android P-9.0.0_r53 Logcat主缓冲区超出定义大小
- 套接字读取后,我在缓冲区中看到意外输入
- std::带有自定义缓冲区的 iostream 不允许我写入
- 为什么我需要C++中不同的排序格式来对这个USACO代码上的数组和优先级队列进行排序
- 从返回的顶点缓冲区查询顶点结构
- Vulkan 中的动态顶点缓冲区格式设置
- 没有优先级反转的环形缓冲区