C++ 无优先级反转的环形缓冲区
我有一个高优先级进程,需要将数据传递给低优先级进程。我编写了一个基本的环形缓冲区来处理数据传递:C++ 无优先级反转的环形缓冲区,c++,concurrency,circular-buffer,C++,Concurrency,Circular Buffer,我有一个高优先级进程,需要将数据传递给低优先级进程。我编写了一个基本的环形缓冲区来处理数据传递: class RingBuffer { public: RingBuffer(int size); ~RingBuffer(); int count() {return (size + end - start) % size;} void write(char *data, int bytes) { // some work that uses on
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
的更新和访问是原子的。这可能是自动的,例如Microsoft声明:end
- 您可以考虑这样一个事实,
即使在您获得值时也可能已过期。在读取线程中,count
将返回您可以依赖的最小计数;对于写入线程,count
将返回最大计数,真实计数可能更低count
- Boost提供了一个循环缓冲区,但它不是线程安全的。不幸的是,我不知道有任何实现是这样的
即将到来的C++标准将原子操作添加到标准库中,因此它们将来可用,但大多数实现还不支持它们。 我看不到任何跨平台的解决方案可以在两个线程都写入时保持
count
正常,除非实现锁定
通常,您可能会使用消息传递系统,强制低优先级线程请求高优先级线程进行更新或类似操作。例如,如果低优先级线程消耗15个字节,它应该要求高优先级线程将计数减少15
本质上,您将限制对高优先级线程的“写”访问,而只允许低优先级线程读取。这样,您可以避免所有锁定,高优先级线程不必担心等待较低线程完成写入,使高优先级线程真正具有高优先级。
boost::interprocess
在boost/interprocess/detail/atomic中提供跨平台原子函数。hpp
编译器是否存在将更新拆分为非原子步骤序列的风险?即,将write()的最后一行转换为end+=字节;结束%=大小代码>?编译器可以非原子地计算RHS,根据您的平台,甚至可能非原子地存储int(即,在8位平台上使用两次8位写入来存储16位int)。@user168715,如果您绝对不相信编译器会做正确的事,创建一个volatile
临时变量来保存计算,然后复制它以进行更新。您对所使用的平台有任何限制吗?@Peter让我们假设x86(其中IIRC 32位写入对齐地址是原子的),但越是可移植越好。下面是一个队列,它的运行基本上是无等待的